register_allocator_test.cc revision 5e8b137d28c840b128e2488f954cccee3e86db14
1/* 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" 19#include "code_generator_x86.h" 20#include "dex_file.h" 21#include "dex_instruction.h" 22#include "driver/compiler_options.h" 23#include "nodes.h" 24#include "optimizing_unit_test.h" 25#include "register_allocator.h" 26#include "ssa_liveness_analysis.h" 27#include "ssa_phi_elimination.h" 28#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 HGraph* graph = new (&allocator) HGraph(&allocator); 41 HGraphBuilder builder(graph); 42 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); 43 builder.BuildGraph(*item); 44 graph->TryBuildingSsa(); 45 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 46 SsaLivenessAnalysis liveness(*graph, &codegen); 47 liveness.Analyze(); 48 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 49 register_allocator.AllocateRegisters(); 50 return register_allocator.Validate(false); 51} 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); 61 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 62 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)); 69 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 70 intervals, 0, 0, codegen, &allocator, true, false)); 71 72 intervals.Get(1)->SetRegister(0); 73 ASSERT_FALSE(RegisterAllocator::ValidateIntervals( 74 intervals, 0, 0, codegen, &allocator, true, false)); 75 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)); 84 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 85 intervals, 0, 0, codegen, &allocator, true, false)); 86 87 intervals.Get(1)->SetRegister(0); 88 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 89 intervals, 0, 0, codegen, &allocator, true, false)); 90 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)); 99 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 100 intervals, 0, 0, codegen, &allocator, true, false)); 101 102 intervals.Get(1)->SetRegister(0); 103 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 104 intervals, 0, 0, codegen, &allocator, true, false)); 105 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)); 114 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 115 intervals, 0, 0, codegen, &allocator, true, false)); 116 117 intervals.Get(1)->SetRegister(0); 118 ASSERT_FALSE(RegisterAllocator::ValidateIntervals( 119 intervals, 0, 0, codegen, &allocator, true, false)); 120 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)); 130 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 131 intervals, 0, 0, codegen, &allocator, true, false)); 132 133 intervals.Get(1)->SetRegister(0); 134 // Sibling of the first interval has no register allocated to it. 135 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 136 intervals, 0, 0, codegen, &allocator, true, false)); 137 138 intervals.Get(0)->GetNextSibling()->SetRegister(0); 139 ASSERT_FALSE(RegisterAllocator::ValidateIntervals( 140 intervals, 0, 0, codegen, &allocator, true, false)); 141 } 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 HGraph* graph = new (allocator) HGraph(allocator); 254 HGraphBuilder builder(graph); 255 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); 256 builder.BuildGraph(*item); 257 graph->TryBuildingSsa(); 258 return graph; 259} 260 261TEST(RegisterAllocatorTest, Loop3) { 262 /* 263 * Test the following snippet: 264 * int a = 0 265 * do { 266 * b = a; 267 * a++; 268 * } while (a != 5) 269 * return b; 270 * 271 * Which becomes the following graph: 272 * constant0 273 * constant1 274 * constant5 275 * goto 276 * | 277 * goto 278 * |++++++++++++ 279 * phi + 280 * a++ + 281 * equals + 282 * if + 283 * |++++++++++++ 284 * return 285 * | 286 * exit 287 */ 288 289 const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( 290 Instruction::CONST_4 | 0 | 0, 291 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8, 292 Instruction::CONST_4 | 5 << 12 | 2 << 8, 293 Instruction::IF_NE | 1 << 8 | 2 << 12, 3, 294 Instruction::RETURN | 0 << 8, 295 Instruction::MOVE | 1 << 12 | 0 << 8, 296 Instruction::GOTO | 0xF900); 297 298 ArenaPool pool; 299 ArenaAllocator allocator(&pool); 300 HGraph* graph = BuildSSAGraph(data, &allocator); 301 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 302 SsaLivenessAnalysis liveness(*graph, &codegen); 303 liveness.Analyze(); 304 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 305 register_allocator.AllocateRegisters(); 306 ASSERT_TRUE(register_allocator.Validate(false)); 307 308 HBasicBlock* loop_header = graph->GetBlocks().Get(2); 309 HPhi* phi = loop_header->GetFirstPhi()->AsPhi(); 310 311 LiveInterval* phi_interval = phi->GetLiveInterval(); 312 LiveInterval* loop_update = phi->InputAt(1)->GetLiveInterval(); 313 ASSERT_TRUE(phi_interval->HasRegister()); 314 ASSERT_TRUE(loop_update->HasRegister()); 315 ASSERT_NE(phi_interval->GetRegister(), loop_update->GetRegister()); 316 317 HBasicBlock* return_block = graph->GetBlocks().Get(3); 318 HReturn* ret = return_block->GetLastInstruction()->AsReturn(); 319 ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister()); 320} 321 322TEST(RegisterAllocatorTest, FirstRegisterUse) { 323 const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( 324 Instruction::CONST_4 | 0 | 0, 325 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8, 326 Instruction::ADD_INT_LIT8 | 0 << 8, 1 << 8, 327 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8 | 1, 328 Instruction::RETURN_VOID); 329 330 ArenaPool pool; 331 ArenaAllocator allocator(&pool); 332 HGraph* graph = BuildSSAGraph(data, &allocator); 333 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 334 SsaLivenessAnalysis liveness(*graph, &codegen); 335 liveness.Analyze(); 336 337 HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd(); 338 HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd(); 339 ASSERT_EQ(last_add->InputAt(0), first_add); 340 LiveInterval* interval = first_add->GetLiveInterval(); 341 ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition()); 342 ASSERT_TRUE(interval->GetNextSibling() == nullptr); 343 344 // We need a register for the output of the instruction. 345 ASSERT_EQ(interval->FirstRegisterUse(), first_add->GetLifetimePosition()); 346 347 // Split at the next instruction. 348 interval = interval->SplitAt(first_add->GetLifetimePosition() + 2); 349 // The user of the split is the last add. 350 ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition()); 351 352 // Split before the last add. 353 LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1); 354 // Ensure the current interval has no register use... 355 ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime); 356 // And the new interval has it for the last add. 357 ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition()); 358} 359 360TEST(RegisterAllocatorTest, DeadPhi) { 361 /* Test for a dead loop phi taking as back-edge input a phi that also has 362 * this loop phi as input. Walking backwards in SsaDeadPhiElimination 363 * does not solve the problem because the loop phi will be visited last. 364 * 365 * Test the following snippet: 366 * int a = 0 367 * do { 368 * if (true) { 369 * a = 2; 370 * } 371 * } while (true); 372 */ 373 374 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 375 Instruction::CONST_4 | 0 | 0, 376 Instruction::CONST_4 | 1 << 8 | 0, 377 Instruction::IF_NE | 1 << 8 | 1 << 12, 3, 378 Instruction::CONST_4 | 2 << 12 | 0 << 8, 379 Instruction::GOTO | 0xFD00, 380 Instruction::RETURN_VOID); 381 382 ArenaPool pool; 383 ArenaAllocator allocator(&pool); 384 HGraph* graph = BuildSSAGraph(data, &allocator); 385 SsaDeadPhiElimination(graph).Run(); 386 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 387 SsaLivenessAnalysis liveness(*graph, &codegen); 388 liveness.Analyze(); 389 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 390 register_allocator.AllocateRegisters(); 391 ASSERT_TRUE(register_allocator.Validate(false)); 392} 393 394/** 395 * Test that the TryAllocateFreeReg method works in the presence of inactive intervals 396 * that share the same register. It should split the interval it is currently 397 * allocating for at the minimum lifetime position between the two inactive intervals. 398 */ 399TEST(RegisterAllocatorTest, FreeUntil) { 400 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 401 Instruction::CONST_4 | 0 | 0, 402 Instruction::RETURN); 403 404 ArenaPool pool; 405 ArenaAllocator allocator(&pool); 406 HGraph* graph = BuildSSAGraph(data, &allocator); 407 SsaDeadPhiElimination(graph).Run(); 408 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 409 SsaLivenessAnalysis liveness(*graph, &codegen); 410 liveness.Analyze(); 411 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 412 413 // Add an artifical range to cover the temps that will be put in the unhandled list. 414 LiveInterval* unhandled = graph->GetEntryBlock()->GetFirstInstruction()->GetLiveInterval(); 415 unhandled->AddLoopRange(0, 60); 416 // For SSA value intervals, only an interval resulted from a split may intersect 417 // with inactive intervals. 418 unhandled = register_allocator.Split(unhandled, 5); 419 420 // Add three temps holding the same register, and starting at different positions. 421 // Put the one that should be picked in the middle of the inactive list to ensure 422 // we do not depend on an order. 423 LiveInterval* interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt); 424 interval->SetRegister(0); 425 interval->AddRange(40, 50); 426 register_allocator.inactive_.Add(interval); 427 428 interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt); 429 interval->SetRegister(0); 430 interval->AddRange(20, 30); 431 register_allocator.inactive_.Add(interval); 432 433 interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt); 434 interval->SetRegister(0); 435 interval->AddRange(60, 70); 436 register_allocator.inactive_.Add(interval); 437 438 register_allocator.number_of_registers_ = 1; 439 register_allocator.registers_array_ = allocator.AllocArray<size_t>(1); 440 register_allocator.processing_core_registers_ = true; 441 register_allocator.unhandled_ = ®ister_allocator.unhandled_core_intervals_; 442 443 ASSERT_TRUE(register_allocator.TryAllocateFreeReg(unhandled)); 444 445 // Check that we have split the interval. 446 ASSERT_EQ(1u, register_allocator.unhandled_->Size()); 447 // Check that we know need to find a new register where the next interval 448 // that uses the register starts. 449 ASSERT_EQ(20u, register_allocator.unhandled_->Get(0)->GetStart()); 450} 451 452static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator, 453 HPhi** phi, 454 HInstruction** input1, 455 HInstruction** input2) { 456 HGraph* graph = new (allocator) HGraph(allocator); 457 HBasicBlock* entry = new (allocator) HBasicBlock(graph); 458 graph->AddBlock(entry); 459 graph->SetEntryBlock(entry); 460 HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot); 461 entry->AddInstruction(parameter); 462 463 HBasicBlock* block = new (allocator) HBasicBlock(graph); 464 graph->AddBlock(block); 465 entry->AddSuccessor(block); 466 467 HInstruction* test = new (allocator) HInstanceFieldGet( 468 parameter, Primitive::kPrimBoolean, MemberOffset(22), false); 469 block->AddInstruction(test); 470 block->AddInstruction(new (allocator) HIf(test)); 471 HBasicBlock* then = new (allocator) HBasicBlock(graph); 472 HBasicBlock* else_ = new (allocator) HBasicBlock(graph); 473 HBasicBlock* join = new (allocator) HBasicBlock(graph); 474 graph->AddBlock(then); 475 graph->AddBlock(else_); 476 graph->AddBlock(join); 477 478 block->AddSuccessor(then); 479 block->AddSuccessor(else_); 480 then->AddSuccessor(join); 481 else_->AddSuccessor(join); 482 then->AddInstruction(new (allocator) HGoto()); 483 else_->AddInstruction(new (allocator) HGoto()); 484 485 *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt); 486 join->AddPhi(*phi); 487 *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, 488 MemberOffset(42), false); 489 *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, 490 MemberOffset(42), false); 491 then->AddInstruction(*input1); 492 else_->AddInstruction(*input2); 493 join->AddInstruction(new (allocator) HExit()); 494 (*phi)->AddInput(*input1); 495 (*phi)->AddInput(*input2); 496 497 graph->BuildDominatorTree(); 498 graph->AnalyzeNaturalLoops(); 499 return graph; 500} 501 502TEST(RegisterAllocatorTest, PhiHint) { 503 ArenaPool pool; 504 ArenaAllocator allocator(&pool); 505 HPhi *phi; 506 HInstruction *input1, *input2; 507 508 { 509 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2); 510 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 511 SsaLivenessAnalysis liveness(*graph, &codegen); 512 liveness.Analyze(); 513 514 // Check that the register allocator is deterministic. 515 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 516 register_allocator.AllocateRegisters(); 517 518 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 0); 519 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 0); 520 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 0); 521 } 522 523 { 524 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2); 525 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 526 SsaLivenessAnalysis liveness(*graph, &codegen); 527 liveness.Analyze(); 528 529 // Set the phi to a specific register, and check that the inputs get allocated 530 // the same register. 531 phi->GetLocations()->SetOut(Location::RegisterLocation(2)); 532 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 533 register_allocator.AllocateRegisters(); 534 535 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2); 536 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2); 537 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2); 538 } 539 540 { 541 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2); 542 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 543 SsaLivenessAnalysis liveness(*graph, &codegen); 544 liveness.Analyze(); 545 546 // Set input1 to a specific register, and check that the phi and other input get allocated 547 // the same register. 548 input1->GetLocations()->SetOut(Location::RegisterLocation(2)); 549 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 550 register_allocator.AllocateRegisters(); 551 552 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2); 553 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2); 554 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2); 555 } 556 557 { 558 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2); 559 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 560 SsaLivenessAnalysis liveness(*graph, &codegen); 561 liveness.Analyze(); 562 563 // Set input2 to a specific register, and check that the phi and other input get allocated 564 // the same register. 565 input2->GetLocations()->SetOut(Location::RegisterLocation(2)); 566 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 567 register_allocator.AllocateRegisters(); 568 569 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2); 570 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2); 571 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2); 572 } 573} 574 575static HGraph* BuildFieldReturn(ArenaAllocator* allocator, 576 HInstruction** field, 577 HInstruction** ret) { 578 HGraph* graph = new (allocator) HGraph(allocator); 579 HBasicBlock* entry = new (allocator) HBasicBlock(graph); 580 graph->AddBlock(entry); 581 graph->SetEntryBlock(entry); 582 HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot); 583 entry->AddInstruction(parameter); 584 585 HBasicBlock* block = new (allocator) HBasicBlock(graph); 586 graph->AddBlock(block); 587 entry->AddSuccessor(block); 588 589 *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, 590 MemberOffset(42), false); 591 block->AddInstruction(*field); 592 *ret = new (allocator) HReturn(*field); 593 block->AddInstruction(*ret); 594 595 HBasicBlock* exit = new (allocator) HBasicBlock(graph); 596 graph->AddBlock(exit); 597 block->AddSuccessor(exit); 598 exit->AddInstruction(new (allocator) HExit()); 599 return graph; 600} 601 602TEST(RegisterAllocatorTest, ExpectedInRegisterHint) { 603 ArenaPool pool; 604 ArenaAllocator allocator(&pool); 605 HInstruction *field, *ret; 606 607 { 608 HGraph* graph = BuildFieldReturn(&allocator, &field, &ret); 609 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 610 SsaLivenessAnalysis liveness(*graph, &codegen); 611 liveness.Analyze(); 612 613 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 614 register_allocator.AllocateRegisters(); 615 616 // Sanity check that in normal conditions, the register should be hinted to 0 (EAX). 617 ASSERT_EQ(field->GetLiveInterval()->GetRegister(), 0); 618 } 619 620 { 621 HGraph* graph = BuildFieldReturn(&allocator, &field, &ret); 622 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 623 SsaLivenessAnalysis liveness(*graph, &codegen); 624 liveness.Analyze(); 625 626 // Check that the field gets put in the register expected by its use. 627 // Don't use SetInAt because we are overriding an already allocated location. 628 ret->GetLocations()->inputs_.Put(0, Location::RegisterLocation(2)); 629 630 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 631 register_allocator.AllocateRegisters(); 632 633 ASSERT_EQ(field->GetLiveInterval()->GetRegister(), 2); 634 } 635} 636 637static HGraph* BuildTwoAdds(ArenaAllocator* allocator, 638 HInstruction** first_add, 639 HInstruction** second_add) { 640 HGraph* graph = new (allocator) HGraph(allocator); 641 HBasicBlock* entry = new (allocator) HBasicBlock(graph); 642 graph->AddBlock(entry); 643 graph->SetEntryBlock(entry); 644 HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimInt); 645 HInstruction* constant1 = new (allocator) HIntConstant(0); 646 HInstruction* constant2 = new (allocator) HIntConstant(0); 647 entry->AddInstruction(parameter); 648 entry->AddInstruction(constant1); 649 entry->AddInstruction(constant2); 650 651 HBasicBlock* block = new (allocator) HBasicBlock(graph); 652 graph->AddBlock(block); 653 entry->AddSuccessor(block); 654 655 *first_add = new (allocator) HAdd(Primitive::kPrimInt, parameter, constant1); 656 block->AddInstruction(*first_add); 657 *second_add = new (allocator) HAdd(Primitive::kPrimInt, *first_add, constant2); 658 block->AddInstruction(*second_add); 659 660 block->AddInstruction(new (allocator) HExit()); 661 return graph; 662} 663 664TEST(RegisterAllocatorTest, SameAsFirstInputHint) { 665 ArenaPool pool; 666 ArenaAllocator allocator(&pool); 667 HInstruction *first_add, *second_add; 668 669 { 670 HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add); 671 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 672 SsaLivenessAnalysis liveness(*graph, &codegen); 673 liveness.Analyze(); 674 675 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 676 register_allocator.AllocateRegisters(); 677 678 // Sanity check that in normal conditions, the registers are the same. 679 ASSERT_EQ(first_add->GetLiveInterval()->GetRegister(), 1); 680 ASSERT_EQ(second_add->GetLiveInterval()->GetRegister(), 1); 681 } 682 683 { 684 HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add); 685 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 686 SsaLivenessAnalysis liveness(*graph, &codegen); 687 liveness.Analyze(); 688 689 // check that both adds get the same register. 690 // Don't use SetOutput because output is already allocated. 691 first_add->InputAt(0)->GetLocations()->output_ = Location::RegisterLocation(2); 692 ASSERT_EQ(first_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput); 693 ASSERT_EQ(second_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput); 694 695 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 696 register_allocator.AllocateRegisters(); 697 698 ASSERT_EQ(first_add->GetLiveInterval()->GetRegister(), 2); 699 ASSERT_EQ(second_add->GetLiveInterval()->GetRegister(), 2); 700 } 701} 702 703static HGraph* BuildDiv(ArenaAllocator* allocator, 704 HInstruction** div) { 705 HGraph* graph = new (allocator) HGraph(allocator); 706 HBasicBlock* entry = new (allocator) HBasicBlock(graph); 707 graph->AddBlock(entry); 708 graph->SetEntryBlock(entry); 709 HInstruction* first = new (allocator) HParameterValue(0, Primitive::kPrimInt); 710 HInstruction* second = new (allocator) HParameterValue(0, Primitive::kPrimInt); 711 entry->AddInstruction(first); 712 entry->AddInstruction(second); 713 714 HBasicBlock* block = new (allocator) HBasicBlock(graph); 715 graph->AddBlock(block); 716 entry->AddSuccessor(block); 717 718 *div = new (allocator) HDiv(Primitive::kPrimInt, first, second, 0); // don't care about dex_pc. 719 block->AddInstruction(*div); 720 721 block->AddInstruction(new (allocator) HExit()); 722 return graph; 723} 724 725TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) { 726 ArenaPool pool; 727 ArenaAllocator allocator(&pool); 728 HInstruction *div; 729 730 { 731 HGraph* graph = BuildDiv(&allocator, &div); 732 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 733 SsaLivenessAnalysis liveness(*graph, &codegen); 734 liveness.Analyze(); 735 736 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 737 register_allocator.AllocateRegisters(); 738 739 // div on x86 requires its first input in eax and the output be the same as the first input. 740 ASSERT_EQ(div->GetLiveInterval()->GetRegister(), 0); 741 } 742} 743 744// Test a bug in the register allocator, where allocating a blocked 745// register would lead to spilling an inactive interval at the wrong 746// position. 747TEST(RegisterAllocatorTest, SpillInactive) { 748 ArenaPool pool; 749 750 // Create a synthesized graph to please the register_allocator and 751 // ssa_liveness_analysis code. 752 ArenaAllocator allocator(&pool); 753 HGraph* graph = new (&allocator) HGraph(&allocator); 754 HBasicBlock* entry = new (&allocator) HBasicBlock(graph); 755 graph->AddBlock(entry); 756 graph->SetEntryBlock(entry); 757 HInstruction* one = new (&allocator) HParameterValue(0, Primitive::kPrimInt); 758 HInstruction* two = new (&allocator) HParameterValue(0, Primitive::kPrimInt); 759 HInstruction* three = new (&allocator) HParameterValue(0, Primitive::kPrimInt); 760 HInstruction* four = new (&allocator) HParameterValue(0, Primitive::kPrimInt); 761 entry->AddInstruction(one); 762 entry->AddInstruction(two); 763 entry->AddInstruction(three); 764 entry->AddInstruction(four); 765 766 HBasicBlock* block = new (&allocator) HBasicBlock(graph); 767 graph->AddBlock(block); 768 entry->AddSuccessor(block); 769 block->AddInstruction(new (&allocator) HExit()); 770 771 // We create a synthesized user requesting a register, to avoid just spilling the 772 // intervals. 773 HPhi* user = new (&allocator) HPhi(&allocator, 0, 1, Primitive::kPrimInt); 774 user->AddInput(one); 775 user->SetBlock(block); 776 LocationSummary* locations = new (&allocator) LocationSummary(user, LocationSummary::kNoCall); 777 locations->SetInAt(0, Location::RequiresRegister()); 778 static constexpr size_t phi_ranges[][2] = {{20, 30}}; 779 BuildInterval(phi_ranges, arraysize(phi_ranges), &allocator, -1, user); 780 781 // Create an interval with lifetime holes. 782 static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}}; 783 LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), &allocator, -1, one); 784 first->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, first->first_use_); 785 first->first_use_ = new(&allocator) UsePosition(user, 0, false, 7, first->first_use_); 786 first->first_use_ = new(&allocator) UsePosition(user, 0, false, 6, first->first_use_); 787 788 locations = new (&allocator) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall); 789 locations->SetOut(Location::RequiresRegister()); 790 first = first->SplitAt(1); 791 792 // Create an interval that conflicts with the next interval, to force the next 793 // interval to call `AllocateBlockedReg`. 794 static constexpr size_t ranges2[][2] = {{2, 4}}; 795 LiveInterval* second = BuildInterval(ranges2, arraysize(ranges2), &allocator, -1, two); 796 locations = new (&allocator) LocationSummary(second->GetDefinedBy(), LocationSummary::kNoCall); 797 locations->SetOut(Location::RequiresRegister()); 798 799 // Create an interval that will lead to splitting the first interval. The bug occured 800 // by splitting at a wrong position, in this case at the next intersection between 801 // this interval and the first interval. We would have then put the interval with ranges 802 // "[0, 2(, [4, 6(" in the list of handled intervals, even though we haven't processed intervals 803 // before lifetime position 6 yet. 804 static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}}; 805 LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), &allocator, -1, three); 806 third->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, third->first_use_); 807 third->first_use_ = new(&allocator) UsePosition(user, 0, false, 4, third->first_use_); 808 third->first_use_ = new(&allocator) UsePosition(user, 0, false, 3, third->first_use_); 809 locations = new (&allocator) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall); 810 locations->SetOut(Location::RequiresRegister()); 811 third = third->SplitAt(3); 812 813 // Because the first part of the split interval was considered handled, this interval 814 // was free to allocate the same register, even though it conflicts with it. 815 static constexpr size_t ranges4[][2] = {{4, 6}}; 816 LiveInterval* fourth = BuildInterval(ranges4, arraysize(ranges4), &allocator, -1, four); 817 locations = new (&allocator) LocationSummary(fourth->GetDefinedBy(), LocationSummary::kNoCall); 818 locations->SetOut(Location::RequiresRegister()); 819 820 x86::CodeGeneratorX86 codegen(graph, CompilerOptions()); 821 SsaLivenessAnalysis liveness(*graph, &codegen); 822 823 RegisterAllocator register_allocator(&allocator, &codegen, liveness); 824 register_allocator.unhandled_core_intervals_.Add(fourth); 825 register_allocator.unhandled_core_intervals_.Add(third); 826 register_allocator.unhandled_core_intervals_.Add(second); 827 register_allocator.unhandled_core_intervals_.Add(first); 828 829 // Set just one register available to make all intervals compete for the same. 830 register_allocator.number_of_registers_ = 1; 831 register_allocator.registers_array_ = allocator.AllocArray<size_t>(1); 832 register_allocator.processing_core_registers_ = true; 833 register_allocator.unhandled_ = ®ister_allocator.unhandled_core_intervals_; 834 register_allocator.LinearScan(); 835 836 // Test that there is no conflicts between intervals. 837 GrowableArray<LiveInterval*> intervals(&allocator, 0); 838 intervals.Add(first); 839 intervals.Add(second); 840 intervals.Add(third); 841 intervals.Add(fourth); 842 ASSERT_TRUE(RegisterAllocator::ValidateIntervals( 843 intervals, 0, 0, codegen, &allocator, true, false)); 844} 845 846} // namespace art 847