| 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/base/utils/random-number-generator.h" | 5 #include "src/base/utils/random-number-generator.h" |
| 6 #include "src/compiler/register-allocator.h" | 6 #include "src/compiler/register-allocator.h" |
| 7 #include "src/compiler/register-allocator-verifier.h" |
| 7 #include "test/unittests/test-utils.h" | 8 #include "test/unittests/test-utils.h" |
| 8 #include "testing/gmock/include/gmock/gmock.h" | 9 #include "testing/gmock/include/gmock/gmock.h" |
| 9 | 10 |
| 10 namespace v8 { | 11 namespace v8 { |
| 11 namespace internal { | 12 namespace internal { |
| 12 namespace compiler { | 13 namespace compiler { |
| 13 | 14 |
| 14 typedef BasicBlock::RpoNumber Rpo; | 15 typedef BasicBlock::RpoNumber Rpo; |
| 15 | 16 |
| 16 namespace { | 17 namespace { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 30 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); | 31 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); |
| 31 *loc++ = 0; | 32 *loc++ = 0; |
| 32 } | 33 } |
| 33 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { | 34 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { |
| 34 double_register_names_[i] = loc; | 35 double_register_names_[i] = loc; |
| 35 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; | 36 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; |
| 36 *loc++ = 0; | 37 *loc++ = 0; |
| 37 } | 38 } |
| 38 } | 39 } |
| 39 | 40 |
| 40 enum BlockCompletionType { kFallThrough, kBranch, kJump }; | 41 enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump }; |
| 41 | 42 |
| 42 struct BlockCompletion { | 43 struct BlockCompletion { |
| 43 BlockCompletionType type_; | 44 BlockCompletionType type_; |
| 44 int vreg_; | 45 int vreg_; |
| 45 int offset_0_; | 46 int offset_0_; |
| 46 int offset_1_; | 47 int offset_1_; |
| 47 }; | 48 }; |
| 48 | 49 |
| 49 static const int kInvalidJumpOffset = kMinInt; | 50 static const int kInvalidJumpOffset = kMinInt; |
| 50 | 51 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 70 | 71 |
| 71 class RegisterAllocatorTest : public TestWithZone { | 72 class RegisterAllocatorTest : public TestWithZone { |
| 72 public: | 73 public: |
| 73 static const int kDefaultNRegs = 4; | 74 static const int kDefaultNRegs = 4; |
| 74 | 75 |
| 75 RegisterAllocatorTest() | 76 RegisterAllocatorTest() |
| 76 : num_general_registers_(kDefaultNRegs), | 77 : num_general_registers_(kDefaultNRegs), |
| 77 num_double_registers_(kDefaultNRegs), | 78 num_double_registers_(kDefaultNRegs), |
| 78 instruction_blocks_(zone()), | 79 instruction_blocks_(zone()), |
| 79 current_block_(nullptr), | 80 current_block_(nullptr), |
| 80 is_last_block_(false) { | 81 is_last_block_(false), |
| 82 block_returns_(false) { |
| 81 InitializeRegisterNames(); | 83 InitializeRegisterNames(); |
| 82 } | 84 } |
| 83 | 85 |
| 84 void SetNumRegs(int num_general_registers, int num_double_registers) { | 86 void SetNumRegs(int num_general_registers, int num_double_registers) { |
| 85 CHECK(instruction_blocks_.empty()); | 87 CHECK(instruction_blocks_.empty()); |
| 86 num_general_registers_ = num_general_registers; | 88 num_general_registers_ = num_general_registers; |
| 87 num_double_registers_ = num_double_registers; | 89 num_double_registers_ = num_double_registers; |
| 88 } | 90 } |
| 89 | 91 |
| 90 RegisterConfiguration* config() { | 92 RegisterConfiguration* config() { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 } | 137 } |
| 136 | 138 |
| 137 void StartLastBlock() { | 139 void StartLastBlock() { |
| 138 CHECK(!is_last_block_); | 140 CHECK(!is_last_block_); |
| 139 is_last_block_ = true; | 141 is_last_block_ = true; |
| 140 NewBlock(); | 142 NewBlock(); |
| 141 } | 143 } |
| 142 | 144 |
| 143 void StartBlock() { | 145 void StartBlock() { |
| 144 CHECK(!is_last_block_); | 146 CHECK(!is_last_block_); |
| 147 block_returns_ = false; |
| 145 NewBlock(); | 148 NewBlock(); |
| 146 } | 149 } |
| 147 | 150 |
| 148 void EndBlock(BlockCompletion completion = FallThrough()) { | 151 void EndBlock(BlockCompletion completion = FallThrough()) { |
| 149 completions_.push_back(completion); | 152 completions_.push_back(completion); |
| 150 switch (completion.type_) { | 153 switch (completion.type_) { |
| 154 case kBlockEnd: |
| 155 CHECK(false); // unreachable; |
| 156 break; |
| 151 case kFallThrough: | 157 case kFallThrough: |
| 152 if (is_last_block_) break; | 158 if (is_last_block_ || block_returns_) { |
| 153 // TODO(dcarney): we don't emit this after returns. | 159 completions_.back().type_ = kBlockEnd; |
| 160 break; |
| 161 } |
| 154 EmitFallThrough(); | 162 EmitFallThrough(); |
| 155 break; | 163 break; |
| 156 case kJump: | 164 case kJump: |
| 165 CHECK(!block_returns_); |
| 157 EmitJump(); | 166 EmitJump(); |
| 158 break; | 167 break; |
| 159 case kBranch: | 168 case kBranch: |
| 169 CHECK(!block_returns_); |
| 160 EmitBranch(completion.vreg_); | 170 EmitBranch(completion.vreg_); |
| 161 break; | 171 break; |
| 162 } | 172 } |
| 163 CHECK(current_block_ != nullptr); | 173 CHECK(current_block_ != nullptr); |
| 164 sequence()->EndBlock(current_block_->rpo_number()); | 174 sequence()->EndBlock(current_block_->rpo_number()); |
| 165 current_block_ = nullptr; | 175 current_block_ = nullptr; |
| 166 } | 176 } |
| 167 | 177 |
| 168 void Allocate() { | 178 void Allocate() { |
| 169 CHECK_EQ(nullptr, current_block_); | 179 CHECK_EQ(nullptr, current_block_); |
| 170 CHECK(is_last_block_); | 180 CHECK(is_last_block_); |
| 171 WireBlocks(); | 181 WireBlocks(); |
| 182 RegisterAllocatorVerifier verifier(zone(), config(), sequence()); |
| 172 if (FLAG_trace_alloc || FLAG_trace_turbo) { | 183 if (FLAG_trace_alloc || FLAG_trace_turbo) { |
| 173 OFStream os(stdout); | 184 OFStream os(stdout); |
| 174 PrintableInstructionSequence printable = {config(), sequence()}; | 185 PrintableInstructionSequence printable = {config(), sequence()}; |
| 175 os << "Before: " << std::endl << printable << std::endl; | 186 os << "Before: " << std::endl << printable << std::endl; |
| 176 } | 187 } |
| 177 allocator()->Allocate(); | 188 allocator()->Allocate(); |
| 178 if (FLAG_trace_alloc || FLAG_trace_turbo) { | 189 if (FLAG_trace_alloc || FLAG_trace_turbo) { |
| 179 OFStream os(stdout); | 190 OFStream os(stdout); |
| 180 PrintableInstructionSequence printable = {config(), sequence()}; | 191 PrintableInstructionSequence printable = {config(), sequence()}; |
| 181 os << "After: " << std::endl << printable << std::endl; | 192 os << "After: " << std::endl << printable << std::endl; |
| 182 } | 193 } |
| 194 verifier.VerifyAssignment(); |
| 195 verifier.VerifyGapMoves(); |
| 183 } | 196 } |
| 184 | 197 |
| 185 int NewReg() { return sequence()->NextVirtualRegister(); } | 198 int NewReg() { return sequence()->NextVirtualRegister(); } |
| 186 | 199 |
| 187 int Parameter() { | 200 int Parameter() { |
| 188 int vreg = NewReg(); | 201 int vreg = NewReg(); |
| 189 InstructionOperand* outputs[1]{UseRegister(vreg)}; | 202 InstructionOperand* outputs[1]{UseRegister(vreg)}; |
| 190 Emit(kArchNop, 1, outputs); | 203 Emit(kArchNop, 1, outputs); |
| 191 return vreg; | 204 return vreg; |
| 192 } | 205 } |
| 193 | 206 |
| 194 Instruction* Return(int vreg) { | 207 Instruction* Return(int vreg) { |
| 208 block_returns_ = true; |
| 195 InstructionOperand* inputs[1]{UseRegister(vreg)}; | 209 InstructionOperand* inputs[1]{UseRegister(vreg)}; |
| 196 return Emit(kArchRet, 0, nullptr, 1, inputs); | 210 return Emit(kArchRet, 0, nullptr, 1, inputs); |
| 197 } | 211 } |
| 198 | 212 |
| 199 PhiInstruction* Phi(int vreg) { | 213 PhiInstruction* Phi(int vreg) { |
| 200 PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg()); | 214 PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg()); |
| 201 phi->operands().push_back(vreg); | 215 phi->operands().push_back(vreg); |
| 202 current_block_->AddPhi(phi); | 216 current_block_->AddPhi(phi); |
| 203 return phi; | 217 return phi; |
| 204 } | 218 } |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 zone(), block_id, rpo, rpo, loop_header, loop_end, false); | 351 zone(), block_id, rpo, rpo, loop_header, loop_end, false); |
| 338 instruction_blocks_.push_back(instruction_block); | 352 instruction_blocks_.push_back(instruction_block); |
| 339 current_block_ = instruction_block; | 353 current_block_ = instruction_block; |
| 340 sequence()->StartBlock(rpo); | 354 sequence()->StartBlock(rpo); |
| 341 return instruction_block; | 355 return instruction_block; |
| 342 } | 356 } |
| 343 | 357 |
| 344 void WireBlocks() { | 358 void WireBlocks() { |
| 345 CHECK(instruction_blocks_.size() == completions_.size()); | 359 CHECK(instruction_blocks_.size() == completions_.size()); |
| 346 size_t offset = 0; | 360 size_t offset = 0; |
| 347 size_t size = instruction_blocks_.size(); | |
| 348 for (const auto& completion : completions_) { | 361 for (const auto& completion : completions_) { |
| 349 switch (completion.type_) { | 362 switch (completion.type_) { |
| 350 case kFallThrough: | 363 case kBlockEnd: |
| 351 if (offset == size - 1) break; | 364 break; |
| 352 // Fallthrough. | 365 case kFallThrough: // Fallthrough. |
| 353 case kJump: | 366 case kJump: |
| 354 WireBlock(offset, completion.offset_0_); | 367 WireBlock(offset, completion.offset_0_); |
| 355 break; | 368 break; |
| 356 case kBranch: | 369 case kBranch: |
| 357 WireBlock(offset, completion.offset_0_); | 370 WireBlock(offset, completion.offset_0_); |
| 358 WireBlock(offset, completion.offset_1_); | 371 WireBlock(offset, completion.offset_1_); |
| 359 break; | 372 break; |
| 360 } | 373 } |
| 361 ++offset; | 374 ++offset; |
| 362 } | 375 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 386 SmartPointer<InstructionSequence> sequence_; | 399 SmartPointer<InstructionSequence> sequence_; |
| 387 int num_general_registers_; | 400 int num_general_registers_; |
| 388 int num_double_registers_; | 401 int num_double_registers_; |
| 389 | 402 |
| 390 // Block building state. | 403 // Block building state. |
| 391 InstructionBlocks instruction_blocks_; | 404 InstructionBlocks instruction_blocks_; |
| 392 Completions completions_; | 405 Completions completions_; |
| 393 LoopBlocks loop_blocks_; | 406 LoopBlocks loop_blocks_; |
| 394 InstructionBlock* current_block_; | 407 InstructionBlock* current_block_; |
| 395 bool is_last_block_; | 408 bool is_last_block_; |
| 409 bool block_returns_; |
| 396 }; | 410 }; |
| 397 | 411 |
| 398 | 412 |
| 399 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { | 413 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { |
| 400 StartLastBlock(); | 414 StartLastBlock(); |
| 401 int a_reg = Parameter(); | 415 int a_reg = Parameter(); |
| 402 int b_reg = Parameter(); | 416 int b_reg = Parameter(); |
| 403 int c_reg = NewReg(); | 417 int c_reg = NewReg(); |
| 404 Instruction* res = EmitRRR(c_reg, a_reg, b_reg); | 418 Instruction* res = EmitRRR(c_reg, a_reg, b_reg); |
| 405 Return(c_reg); | 419 Return(c_reg); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 504 // Return sum. | 518 // Return sum. |
| 505 Return(DefineConstant()); | 519 Return(DefineConstant()); |
| 506 EndBlock(); | 520 EndBlock(); |
| 507 | 521 |
| 508 Allocate(); | 522 Allocate(); |
| 509 } | 523 } |
| 510 | 524 |
| 511 } // namespace compiler | 525 } // namespace compiler |
| 512 } // namespace internal | 526 } // namespace internal |
| 513 } // namespace v8 | 527 } // namespace v8 |
| OLD | NEW |