| OLD | NEW |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/pipeline.h" | 5 #include "src/compiler/pipeline.h" |
| 6 #include "test/unittests/compiler/instruction-sequence-unittest.h" | 6 #include "test/unittests/compiler/instruction-sequence-unittest.h" |
| 7 | 7 |
| 8 namespace v8 { | 8 namespace v8 { |
| 9 namespace internal { | 9 namespace internal { |
| 10 namespace compiler { | 10 namespace compiler { |
| 11 | 11 |
| 12 |
| 13 namespace { |
| 14 |
| 15 // We can't just use the size of the moves collection, because of |
| 16 // redundant moves which need to be discounted. |
| 17 int GetMoveCount(const ParallelMove& moves) { |
| 18 int move_count = 0; |
| 19 for (auto move : moves) { |
| 20 if (move->IsEliminated() || move->IsRedundant()) continue; |
| 21 ++move_count; |
| 22 } |
| 23 return move_count; |
| 24 } |
| 25 |
| 26 |
| 27 bool AllocatedOperandMatches( |
| 28 AllocatedOperand op, const InstructionSequenceTest::TestOperand& test_op) { |
| 29 return op.IsRegister() == |
| 30 (test_op.type_ == |
| 31 InstructionSequenceTest::TestOperandType::kFixedRegister) && |
| 32 op.index() == test_op.value_; |
| 33 } |
| 34 |
| 35 |
| 36 void CheckNoParallelMoves(int instr_index, Instruction::GapPosition gap_pos, |
| 37 const InstructionSequence* sequence) { |
| 38 const ParallelMove* moves = moves = |
| 39 sequence->InstructionAt(instr_index)->GetParallelMove(gap_pos); |
| 40 EXPECT_TRUE(moves == nullptr || GetMoveCount(*moves) == 0); |
| 41 } |
| 42 |
| 43 |
| 44 void CheckParallelMovePresent( |
| 45 int instr_index, Instruction::GapPosition gap_pos, |
| 46 const InstructionSequence* sequence, |
| 47 const InstructionSequenceTest::TestOperand& src, |
| 48 const InstructionSequenceTest::TestOperand& dest) { |
| 49 const ParallelMove* moves = moves = |
| 50 sequence->InstructionAt(instr_index)->GetParallelMove(gap_pos); |
| 51 EXPECT_NE(nullptr, moves); |
| 52 |
| 53 bool found_match = false; |
| 54 for (auto move : *moves) { |
| 55 if (move->IsEliminated() || move->IsRedundant()) continue; |
| 56 if (AllocatedOperandMatches(AllocatedOperand::cast(move->source()), src) && |
| 57 AllocatedOperandMatches(AllocatedOperand::cast(move->destination()), |
| 58 dest)) { |
| 59 found_match = true; |
| 60 break; |
| 61 } |
| 62 } |
| 63 EXPECT_TRUE(found_match); |
| 64 } |
| 65 } |
| 66 |
| 67 |
| 12 class RegisterAllocatorTest : public InstructionSequenceTest { | 68 class RegisterAllocatorTest : public InstructionSequenceTest { |
| 13 public: | 69 public: |
| 14 void Allocate() { | 70 void Allocate() { |
| 15 WireBlocks(); | 71 WireBlocks(); |
| 16 Pipeline::AllocateRegistersForTesting(config(), sequence(), true); | 72 Pipeline::AllocateRegistersForTesting(config(), sequence(), true); |
| 17 } | 73 } |
| 18 }; | 74 }; |
| 19 | 75 |
| 20 | 76 |
| 21 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { | 77 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 485 StartBlock(); | 541 StartBlock(); |
| 486 // Force c to split within to_spill's definition. | 542 // Force c to split within to_spill's definition. |
| 487 EmitI(Reg(c)); | 543 EmitI(Reg(c)); |
| 488 EmitI(Reg(to_spill)); | 544 EmitI(Reg(to_spill)); |
| 489 EndBlock(Last()); | 545 EndBlock(Last()); |
| 490 | 546 |
| 491 Allocate(); | 547 Allocate(); |
| 492 } | 548 } |
| 493 | 549 |
| 494 | 550 |
| 551 TEST_F(RegisterAllocatorTest, DiamondWithCallFirstBlock) { |
| 552 StartBlock(); |
| 553 auto x = EmitOI(Reg(0)); |
| 554 EndBlock(Branch(Reg(x), 1, 2)); |
| 555 |
| 556 StartBlock(); |
| 557 EmitCall(Slot(-1)); |
| 558 auto occupy = EmitOI(Reg(0)); |
| 559 EndBlock(Jump(2)); |
| 560 |
| 561 StartBlock(); |
| 562 EndBlock(FallThrough()); |
| 563 |
| 564 StartBlock(); |
| 565 Use(occupy); |
| 566 Return(Reg(x)); |
| 567 EndBlock(); |
| 568 Allocate(); |
| 569 } |
| 570 |
| 571 |
| 572 TEST_F(RegisterAllocatorTest, DiamondWithCallSecondBlock) { |
| 573 StartBlock(); |
| 574 auto x = EmitOI(Reg(0)); |
| 575 EndBlock(Branch(Reg(x), 1, 2)); |
| 576 |
| 577 StartBlock(); |
| 578 EndBlock(Jump(2)); |
| 579 |
| 580 StartBlock(); |
| 581 EmitCall(Slot(-1)); |
| 582 auto occupy = EmitOI(Reg(0)); |
| 583 EndBlock(FallThrough()); |
| 584 |
| 585 StartBlock(); |
| 586 Use(occupy); |
| 587 Return(Reg(x)); |
| 588 EndBlock(); |
| 589 Allocate(); |
| 590 } |
| 591 |
| 592 |
| 593 TEST_F(RegisterAllocatorTest, SingleDeferredBlockSpill) { |
| 594 StartBlock(); // B0 |
| 595 auto var = EmitOI(Reg(0)); |
| 596 EndBlock(Branch(Reg(var), 1, 2)); |
| 597 |
| 598 StartBlock(); // B1 |
| 599 EndBlock(Jump(2)); |
| 600 |
| 601 StartBlock(true); // B2 |
| 602 EmitCall(Slot(-1), Slot(var)); |
| 603 EndBlock(); |
| 604 |
| 605 StartBlock(); // B3 |
| 606 EmitNop(); |
| 607 EndBlock(); |
| 608 |
| 609 StartBlock(); // B4 |
| 610 Return(Reg(var, 0)); |
| 611 EndBlock(); |
| 612 |
| 613 Allocate(); |
| 614 |
| 615 const int var_def_index = 1; |
| 616 const int call_index = 3; |
| 617 int expect_no_moves = FLAG_turbo_greedy_regalloc ? var_def_index : call_index; |
| 618 int expect_spill_move = |
| 619 FLAG_turbo_greedy_regalloc ? call_index : var_def_index; |
| 620 |
| 621 // We should have no parallel moves at the "expect_no_moves" position. |
| 622 CheckNoParallelMoves(expect_no_moves, Instruction::START, sequence()); |
| 623 |
| 624 // The spill should be performed at the position expect_spill_move. |
| 625 CheckParallelMovePresent(expect_spill_move, Instruction::START, sequence(), |
| 626 Reg(0), Slot(0)); |
| 627 } |
| 628 |
| 629 |
| 630 TEST_F(RegisterAllocatorTest, MultipleDeferredBlockSpills) { |
| 631 if (!FLAG_turbo_greedy_regalloc) return; |
| 632 |
| 633 StartBlock(); // B0 |
| 634 auto var1 = EmitOI(Reg(0)); |
| 635 auto var2 = EmitOI(Reg(1)); |
| 636 auto var3 = EmitOI(Reg(2)); |
| 637 EndBlock(Branch(Reg(var1, 0), 1, 2)); |
| 638 |
| 639 StartBlock(true); // B1 |
| 640 EmitCall(Slot(-2), Slot(var1)); |
| 641 EndBlock(Jump(2)); |
| 642 |
| 643 StartBlock(true); // B2 |
| 644 EmitCall(Slot(-1), Slot(var2)); |
| 645 EndBlock(); |
| 646 |
| 647 StartBlock(); // B3 |
| 648 EmitNop(); |
| 649 EndBlock(); |
| 650 |
| 651 StartBlock(); // B4 |
| 652 Return(Reg(var3, 2)); |
| 653 EndBlock(); |
| 654 |
| 655 const int call_in_b1 = 4; |
| 656 const int call_in_b2 = 6; |
| 657 const int end_of_b1 = 5; |
| 658 const int end_of_b2 = 7; |
| 659 const int start_of_b3 = 8; |
| 660 |
| 661 Allocate(); |
| 662 // TODO(mtrofin): at the moment, the greedy allocator doesn't understand |
| 663 // preferred registers ("hints"), so the assignments are a bit wacky. Correct |
| 664 // the specific registers once that is fixed. |
| 665 const int var1_reg_in_b1 = 3; |
| 666 const int var1_slot = 0; |
| 667 const int var2_reg_in_b2 = 1; |
| 668 const int var2_slot = 1; |
| 669 const int var3_reg = 2; |
| 670 const int var3_slot = 2; |
| 671 const int var3_reg_out = 0; |
| 672 |
| 673 CheckParallelMovePresent(call_in_b1, Instruction::START, sequence(), |
| 674 Reg(var1_reg_in_b1), Slot(var1_slot)); |
| 675 CheckParallelMovePresent(call_in_b1, Instruction::START, sequence(), |
| 676 Reg(var3_reg), Slot(var3_slot)); |
| 677 CheckParallelMovePresent(end_of_b1, Instruction::START, sequence(), |
| 678 Slot(var3_slot), Reg(var3_reg_out)); |
| 679 |
| 680 CheckParallelMovePresent(call_in_b2, Instruction::START, sequence(), |
| 681 Reg(var2_reg_in_b2), Slot(var2_slot)); |
| 682 CheckParallelMovePresent(call_in_b2, Instruction::START, sequence(), |
| 683 Reg(var3_reg), Slot(var3_slot)); |
| 684 CheckParallelMovePresent(end_of_b2, Instruction::START, sequence(), |
| 685 Slot(var3_slot), Reg(var3_reg_out)); |
| 686 |
| 687 |
| 688 CheckNoParallelMoves(start_of_b3, Instruction::START, sequence()); |
| 689 } |
| 690 |
| 691 |
| 495 namespace { | 692 namespace { |
| 496 | 693 |
| 497 enum class ParameterType { kFixedSlot, kSlot, kRegister, kFixedRegister }; | 694 enum class ParameterType { kFixedSlot, kSlot, kRegister, kFixedRegister }; |
| 498 | 695 |
| 499 const ParameterType kParameterTypes[] = { | 696 const ParameterType kParameterTypes[] = { |
| 500 ParameterType::kFixedSlot, ParameterType::kSlot, ParameterType::kRegister, | 697 ParameterType::kFixedSlot, ParameterType::kSlot, ParameterType::kRegister, |
| 501 ParameterType::kFixedRegister}; | 698 ParameterType::kFixedRegister}; |
| 502 | 699 |
| 503 class SlotConstraintTest : public RegisterAllocatorTest, | 700 class SlotConstraintTest : public RegisterAllocatorTest, |
| 504 public ::testing::WithParamInterface< | 701 public ::testing::WithParamInterface< |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 568 INSTANTIATE_TEST_CASE_P( | 765 INSTANTIATE_TEST_CASE_P( |
| 569 RegisterAllocatorTest, SlotConstraintTest, | 766 RegisterAllocatorTest, SlotConstraintTest, |
| 570 ::testing::Combine(::testing::ValuesIn(kParameterTypes), | 767 ::testing::Combine(::testing::ValuesIn(kParameterTypes), |
| 571 ::testing::Range(0, SlotConstraintTest::kMaxVariant))); | 768 ::testing::Range(0, SlotConstraintTest::kMaxVariant))); |
| 572 | 769 |
| 573 #endif // GTEST_HAS_COMBINE | 770 #endif // GTEST_HAS_COMBINE |
| 574 | 771 |
| 575 } // namespace compiler | 772 } // namespace compiler |
| 576 } // namespace internal | 773 } // namespace internal |
| 577 } // namespace v8 | 774 } // namespace v8 |
| OLD | NEW |