| 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" | |
| 6 #include "src/compiler/instruction.h" | |
| 7 #include "src/compiler/pipeline.h" | 5 #include "src/compiler/pipeline.h" |
| 8 #include "test/unittests/test-utils.h" | 6 #include "test/unittests/compiler/instruction-sequence-unittest.h" |
| 9 #include "testing/gmock/include/gmock/gmock.h" | |
| 10 | 7 |
| 11 namespace v8 { | 8 namespace v8 { |
| 12 namespace internal { | 9 namespace internal { |
| 13 namespace compiler { | 10 namespace compiler { |
| 14 | 11 |
| 15 typedef BasicBlock::RpoNumber Rpo; | 12 class RegisterAllocatorTest : public InstructionSequenceTest { |
| 16 | |
| 17 static const char* | |
| 18 general_register_names_[RegisterConfiguration::kMaxGeneralRegisters]; | |
| 19 static const char* | |
| 20 double_register_names_[RegisterConfiguration::kMaxDoubleRegisters]; | |
| 21 static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters + | |
| 22 RegisterConfiguration::kMaxDoubleRegisters)]; | |
| 23 | |
| 24 static void InitializeRegisterNames() { | |
| 25 char* loc = register_names_; | |
| 26 for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) { | |
| 27 general_register_names_[i] = loc; | |
| 28 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); | |
| 29 *loc++ = 0; | |
| 30 } | |
| 31 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { | |
| 32 double_register_names_[i] = loc; | |
| 33 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; | |
| 34 *loc++ = 0; | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 | |
| 39 class RegisterAllocatorTest : public TestWithZone { | |
| 40 public: | 13 public: |
| 41 static const int kDefaultNRegs = 4; | |
| 42 static const int kNoValue = kMinInt; | |
| 43 | |
| 44 struct VReg { | |
| 45 VReg() : value_(kNoValue) {} | |
| 46 VReg(PhiInstruction* phi) : value_(phi->virtual_register()) {} // NOLINT | |
| 47 explicit VReg(int value) : value_(value) {} | |
| 48 int value_; | |
| 49 }; | |
| 50 | |
| 51 enum TestOperandType { | |
| 52 kInvalid, | |
| 53 kSameAsFirst, | |
| 54 kRegister, | |
| 55 kFixedRegister, | |
| 56 kSlot, | |
| 57 kFixedSlot, | |
| 58 kImmediate, | |
| 59 kNone | |
| 60 }; | |
| 61 | |
| 62 struct TestOperand { | |
| 63 TestOperand() : type_(kInvalid), vreg_(), value_(kNoValue) {} | |
| 64 TestOperand(TestOperandType type, int imm) | |
| 65 : type_(type), vreg_(), value_(imm) {} | |
| 66 TestOperand(TestOperandType type, VReg vreg, int value = kNoValue) | |
| 67 : type_(type), vreg_(vreg), value_(value) {} | |
| 68 | |
| 69 TestOperandType type_; | |
| 70 VReg vreg_; | |
| 71 int value_; | |
| 72 }; | |
| 73 | |
| 74 static TestOperand Same() { return TestOperand(kSameAsFirst, VReg()); } | |
| 75 | |
| 76 static TestOperand Reg(VReg vreg, int index = kNoValue) { | |
| 77 TestOperandType type = kRegister; | |
| 78 if (index != kNoValue) type = kFixedRegister; | |
| 79 return TestOperand(type, vreg, index); | |
| 80 } | |
| 81 | |
| 82 static TestOperand Reg(int index = kNoValue) { return Reg(VReg(), index); } | |
| 83 | |
| 84 static TestOperand Slot(VReg vreg, int index = kNoValue) { | |
| 85 TestOperandType type = kSlot; | |
| 86 if (index != kNoValue) type = kFixedSlot; | |
| 87 return TestOperand(type, vreg, index); | |
| 88 } | |
| 89 | |
| 90 static TestOperand Slot(int index = kNoValue) { return Slot(VReg(), index); } | |
| 91 | |
| 92 static TestOperand Use(VReg vreg) { return TestOperand(kNone, vreg); } | |
| 93 | |
| 94 static TestOperand Use() { return Use(VReg()); } | |
| 95 | |
| 96 enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump }; | |
| 97 | |
| 98 struct BlockCompletion { | |
| 99 BlockCompletionType type_; | |
| 100 TestOperand op_; | |
| 101 int offset_0_; | |
| 102 int offset_1_; | |
| 103 }; | |
| 104 | |
| 105 static BlockCompletion FallThrough() { | |
| 106 BlockCompletion completion = {kFallThrough, TestOperand(), 1, kNoValue}; | |
| 107 return completion; | |
| 108 } | |
| 109 | |
| 110 static BlockCompletion Jump(int offset) { | |
| 111 BlockCompletion completion = {kJump, TestOperand(), offset, kNoValue}; | |
| 112 return completion; | |
| 113 } | |
| 114 | |
| 115 static BlockCompletion Branch(TestOperand op, int left_offset, | |
| 116 int right_offset) { | |
| 117 BlockCompletion completion = {kBranch, op, left_offset, right_offset}; | |
| 118 return completion; | |
| 119 } | |
| 120 | |
| 121 static BlockCompletion Last() { | |
| 122 BlockCompletion completion = {kBlockEnd, TestOperand(), kNoValue, kNoValue}; | |
| 123 return completion; | |
| 124 } | |
| 125 | |
| 126 RegisterAllocatorTest() | |
| 127 : frame_(nullptr), | |
| 128 sequence_(nullptr), | |
| 129 num_general_registers_(kDefaultNRegs), | |
| 130 num_double_registers_(kDefaultNRegs), | |
| 131 instruction_blocks_(zone()), | |
| 132 current_instruction_index_(-1), | |
| 133 current_block_(nullptr), | |
| 134 block_returns_(false) { | |
| 135 InitializeRegisterNames(); | |
| 136 } | |
| 137 | |
| 138 void SetNumRegs(int num_general_registers, int num_double_registers) { | |
| 139 CHECK(config_.is_empty()); | |
| 140 CHECK(instructions_.empty()); | |
| 141 CHECK(instruction_blocks_.empty()); | |
| 142 num_general_registers_ = num_general_registers; | |
| 143 num_double_registers_ = num_double_registers; | |
| 144 } | |
| 145 | |
| 146 RegisterConfiguration* config() { | |
| 147 if (config_.is_empty()) { | |
| 148 config_.Reset(new RegisterConfiguration( | |
| 149 num_general_registers_, num_double_registers_, num_double_registers_, | |
| 150 general_register_names_, double_register_names_)); | |
| 151 } | |
| 152 return config_.get(); | |
| 153 } | |
| 154 | |
| 155 Frame* frame() { | |
| 156 if (frame_ == nullptr) { | |
| 157 frame_ = new (zone()) Frame(); | |
| 158 } | |
| 159 return frame_; | |
| 160 } | |
| 161 | |
| 162 InstructionSequence* sequence() { | |
| 163 if (sequence_ == nullptr) { | |
| 164 sequence_ = | |
| 165 new (zone()) InstructionSequence(zone(), &instruction_blocks_); | |
| 166 } | |
| 167 return sequence_; | |
| 168 } | |
| 169 | |
| 170 void StartLoop(int loop_blocks) { | |
| 171 CHECK(current_block_ == nullptr); | |
| 172 if (!loop_blocks_.empty()) { | |
| 173 CHECK(!loop_blocks_.back().loop_header_.IsValid()); | |
| 174 } | |
| 175 LoopData loop_data = {Rpo::Invalid(), loop_blocks}; | |
| 176 loop_blocks_.push_back(loop_data); | |
| 177 } | |
| 178 | |
| 179 void EndLoop() { | |
| 180 CHECK(current_block_ == nullptr); | |
| 181 CHECK(!loop_blocks_.empty()); | |
| 182 CHECK_EQ(0, loop_blocks_.back().expected_blocks_); | |
| 183 loop_blocks_.pop_back(); | |
| 184 } | |
| 185 | |
| 186 void StartBlock() { | |
| 187 block_returns_ = false; | |
| 188 NewBlock(); | |
| 189 } | |
| 190 | |
| 191 int EndBlock(BlockCompletion completion = FallThrough()) { | |
| 192 int instruction_index = kMinInt; | |
| 193 if (block_returns_) { | |
| 194 CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough); | |
| 195 completion.type_ = kBlockEnd; | |
| 196 } | |
| 197 switch (completion.type_) { | |
| 198 case kBlockEnd: | |
| 199 break; | |
| 200 case kFallThrough: | |
| 201 instruction_index = EmitFallThrough(); | |
| 202 break; | |
| 203 case kJump: | |
| 204 CHECK(!block_returns_); | |
| 205 instruction_index = EmitJump(); | |
| 206 break; | |
| 207 case kBranch: | |
| 208 CHECK(!block_returns_); | |
| 209 instruction_index = EmitBranch(completion.op_); | |
| 210 break; | |
| 211 } | |
| 212 completions_.push_back(completion); | |
| 213 CHECK(current_block_ != nullptr); | |
| 214 sequence()->EndBlock(current_block_->rpo_number()); | |
| 215 current_block_ = nullptr; | |
| 216 return instruction_index; | |
| 217 } | |
| 218 | |
| 219 void Allocate() { | 14 void Allocate() { |
| 220 CHECK_EQ(nullptr, current_block_); | |
| 221 WireBlocks(); | 15 WireBlocks(); |
| 222 Pipeline::AllocateRegistersForTesting(config(), sequence(), true); | 16 Pipeline::AllocateRegistersForTesting(config(), sequence(), true); |
| 223 } | 17 } |
| 224 | |
| 225 TestOperand Imm(int32_t imm = 0) { | |
| 226 int index = sequence()->AddImmediate(Constant(imm)); | |
| 227 return TestOperand(kImmediate, index); | |
| 228 } | |
| 229 | |
| 230 VReg Parameter(TestOperand output_op = Reg()) { | |
| 231 VReg vreg = NewReg(); | |
| 232 InstructionOperand* outputs[1]{ConvertOutputOp(vreg, output_op)}; | |
| 233 Emit(vreg.value_, kArchNop, 1, outputs); | |
| 234 return vreg; | |
| 235 } | |
| 236 | |
| 237 int Return(TestOperand input_op_0) { | |
| 238 block_returns_ = true; | |
| 239 InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)}; | |
| 240 return Emit(NewIndex(), kArchRet, 0, nullptr, 1, inputs); | |
| 241 } | |
| 242 | |
| 243 int Return(VReg vreg) { return Return(Reg(vreg, 0)); } | |
| 244 | |
| 245 PhiInstruction* Phi(VReg incoming_vreg) { | |
| 246 PhiInstruction* phi = | |
| 247 new (zone()) PhiInstruction(zone(), NewReg().value_, 10); | |
| 248 phi->Extend(zone(), incoming_vreg.value_); | |
| 249 current_block_->AddPhi(phi); | |
| 250 return phi; | |
| 251 } | |
| 252 | |
| 253 PhiInstruction* Phi(VReg incoming_vreg_0, VReg incoming_vreg_1) { | |
| 254 auto phi = Phi(incoming_vreg_0); | |
| 255 Extend(phi, incoming_vreg_1); | |
| 256 return phi; | |
| 257 } | |
| 258 | |
| 259 void Extend(PhiInstruction* phi, VReg vreg) { | |
| 260 phi->Extend(zone(), vreg.value_); | |
| 261 } | |
| 262 | |
| 263 VReg DefineConstant(int32_t imm = 0) { | |
| 264 VReg vreg = NewReg(); | |
| 265 sequence()->AddConstant(vreg.value_, Constant(imm)); | |
| 266 InstructionOperand* outputs[1]{ | |
| 267 ConstantOperand::Create(vreg.value_, zone())}; | |
| 268 Emit(vreg.value_, kArchNop, 1, outputs); | |
| 269 return vreg; | |
| 270 } | |
| 271 | |
| 272 VReg EmitOII(TestOperand output_op, TestOperand input_op_0, | |
| 273 TestOperand input_op_1) { | |
| 274 VReg output_vreg = NewReg(); | |
| 275 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; | |
| 276 InstructionOperand* inputs[2]{ConvertInputOp(input_op_0), | |
| 277 ConvertInputOp(input_op_1)}; | |
| 278 Emit(output_vreg.value_, kArchNop, 1, outputs, 2, inputs); | |
| 279 return output_vreg; | |
| 280 } | |
| 281 | |
| 282 VReg EmitCall(TestOperand output_op, size_t input_size, TestOperand* inputs) { | |
| 283 VReg output_vreg = NewReg(); | |
| 284 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; | |
| 285 InstructionOperand** mapped_inputs = | |
| 286 zone()->NewArray<InstructionOperand*>(static_cast<int>(input_size)); | |
| 287 for (size_t i = 0; i < input_size; ++i) { | |
| 288 mapped_inputs[i] = ConvertInputOp(inputs[i]); | |
| 289 } | |
| 290 Emit(output_vreg.value_, kArchCallCodeObject, 1, outputs, input_size, | |
| 291 mapped_inputs); | |
| 292 return output_vreg; | |
| 293 } | |
| 294 | |
| 295 // Get defining instruction vreg or value returned at instruction creation | |
| 296 // time when there is no return value. | |
| 297 const Instruction* GetInstruction(int instruction_index) { | |
| 298 auto it = instructions_.find(instruction_index); | |
| 299 CHECK(it != instructions_.end()); | |
| 300 return it->second; | |
| 301 } | |
| 302 | |
| 303 private: | |
| 304 VReg NewReg() { return VReg(sequence()->NextVirtualRegister()); } | |
| 305 int NewIndex() { return current_instruction_index_--; } | |
| 306 | |
| 307 static TestOperand Invalid() { return TestOperand(kInvalid, VReg()); } | |
| 308 | |
| 309 int EmitBranch(TestOperand input_op) { | |
| 310 InstructionOperand* inputs[4]{ConvertInputOp(input_op), | |
| 311 ConvertInputOp(Imm()), ConvertInputOp(Imm()), | |
| 312 ConvertInputOp(Imm())}; | |
| 313 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) | | |
| 314 FlagsConditionField::encode(kEqual); | |
| 315 auto instruction = | |
| 316 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl(); | |
| 317 return AddInstruction(NewIndex(), instruction); | |
| 318 } | |
| 319 | |
| 320 int EmitFallThrough() { | |
| 321 auto instruction = NewInstruction(kArchNop, 0, nullptr)->MarkAsControl(); | |
| 322 return AddInstruction(NewIndex(), instruction); | |
| 323 } | |
| 324 | |
| 325 int EmitJump() { | |
| 326 InstructionOperand* inputs[1]{ConvertInputOp(Imm())}; | |
| 327 auto instruction = | |
| 328 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl(); | |
| 329 return AddInstruction(NewIndex(), instruction); | |
| 330 } | |
| 331 | |
| 332 Instruction* NewInstruction(InstructionCode code, size_t outputs_size, | |
| 333 InstructionOperand** outputs, | |
| 334 size_t inputs_size = 0, | |
| 335 InstructionOperand* *inputs = nullptr, | |
| 336 size_t temps_size = 0, | |
| 337 InstructionOperand* *temps = nullptr) { | |
| 338 CHECK_NE(nullptr, current_block_); | |
| 339 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size, | |
| 340 inputs, temps_size, temps); | |
| 341 } | |
| 342 | |
| 343 InstructionOperand* Unallocated(TestOperand op, | |
| 344 UnallocatedOperand::ExtendedPolicy policy) { | |
| 345 auto unallocated = new (zone()) UnallocatedOperand(policy); | |
| 346 unallocated->set_virtual_register(op.vreg_.value_); | |
| 347 return unallocated; | |
| 348 } | |
| 349 | |
| 350 InstructionOperand* Unallocated(TestOperand op, | |
| 351 UnallocatedOperand::ExtendedPolicy policy, | |
| 352 UnallocatedOperand::Lifetime lifetime) { | |
| 353 auto unallocated = new (zone()) UnallocatedOperand(policy, lifetime); | |
| 354 unallocated->set_virtual_register(op.vreg_.value_); | |
| 355 return unallocated; | |
| 356 } | |
| 357 | |
| 358 InstructionOperand* Unallocated(TestOperand op, | |
| 359 UnallocatedOperand::ExtendedPolicy policy, | |
| 360 int index) { | |
| 361 auto unallocated = new (zone()) UnallocatedOperand(policy, index); | |
| 362 unallocated->set_virtual_register(op.vreg_.value_); | |
| 363 return unallocated; | |
| 364 } | |
| 365 | |
| 366 InstructionOperand* Unallocated(TestOperand op, | |
| 367 UnallocatedOperand::BasicPolicy policy, | |
| 368 int index) { | |
| 369 auto unallocated = new (zone()) UnallocatedOperand(policy, index); | |
| 370 unallocated->set_virtual_register(op.vreg_.value_); | |
| 371 return unallocated; | |
| 372 } | |
| 373 | |
| 374 InstructionOperand* ConvertInputOp(TestOperand op) { | |
| 375 if (op.type_ == kImmediate) { | |
| 376 CHECK_EQ(op.vreg_.value_, kNoValue); | |
| 377 return ImmediateOperand::Create(op.value_, zone()); | |
| 378 } | |
| 379 CHECK_NE(op.vreg_.value_, kNoValue); | |
| 380 switch (op.type_) { | |
| 381 case kNone: | |
| 382 return Unallocated(op, UnallocatedOperand::NONE, | |
| 383 UnallocatedOperand::USED_AT_START); | |
| 384 case kRegister: | |
| 385 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER, | |
| 386 UnallocatedOperand::USED_AT_START); | |
| 387 case kFixedRegister: | |
| 388 CHECK(0 <= op.value_ && op.value_ < num_general_registers_); | |
| 389 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_); | |
| 390 default: | |
| 391 break; | |
| 392 } | |
| 393 CHECK(false); | |
| 394 return NULL; | |
| 395 } | |
| 396 | |
| 397 InstructionOperand* ConvertOutputOp(VReg vreg, TestOperand op) { | |
| 398 CHECK_EQ(op.vreg_.value_, kNoValue); | |
| 399 op.vreg_ = vreg; | |
| 400 switch (op.type_) { | |
| 401 case kSameAsFirst: | |
| 402 return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT); | |
| 403 case kRegister: | |
| 404 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER); | |
| 405 case kFixedSlot: | |
| 406 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_); | |
| 407 case kFixedRegister: | |
| 408 CHECK(0 <= op.value_ && op.value_ < num_general_registers_); | |
| 409 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_); | |
| 410 default: | |
| 411 break; | |
| 412 } | |
| 413 CHECK(false); | |
| 414 return NULL; | |
| 415 } | |
| 416 | |
| 417 InstructionBlock* NewBlock() { | |
| 418 CHECK(current_block_ == nullptr); | |
| 419 auto block_id = BasicBlock::Id::FromSize(instruction_blocks_.size()); | |
| 420 Rpo rpo = Rpo::FromInt(block_id.ToInt()); | |
| 421 Rpo loop_header = Rpo::Invalid(); | |
| 422 Rpo loop_end = Rpo::Invalid(); | |
| 423 if (!loop_blocks_.empty()) { | |
| 424 auto& loop_data = loop_blocks_.back(); | |
| 425 // This is a loop header. | |
| 426 if (!loop_data.loop_header_.IsValid()) { | |
| 427 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_); | |
| 428 loop_data.expected_blocks_--; | |
| 429 loop_data.loop_header_ = rpo; | |
| 430 } else { | |
| 431 // This is a loop body. | |
| 432 CHECK_NE(0, loop_data.expected_blocks_); | |
| 433 // TODO(dcarney): handle nested loops. | |
| 434 loop_data.expected_blocks_--; | |
| 435 loop_header = loop_data.loop_header_; | |
| 436 } | |
| 437 } | |
| 438 // Construct instruction block. | |
| 439 auto instruction_block = new (zone()) InstructionBlock( | |
| 440 zone(), block_id, rpo, rpo, loop_header, loop_end, false); | |
| 441 instruction_blocks_.push_back(instruction_block); | |
| 442 current_block_ = instruction_block; | |
| 443 sequence()->StartBlock(rpo); | |
| 444 return instruction_block; | |
| 445 } | |
| 446 | |
| 447 void WireBlocks() { | |
| 448 CHECK(instruction_blocks_.size() == completions_.size()); | |
| 449 size_t offset = 0; | |
| 450 for (const auto& completion : completions_) { | |
| 451 switch (completion.type_) { | |
| 452 case kBlockEnd: | |
| 453 break; | |
| 454 case kFallThrough: // Fallthrough. | |
| 455 case kJump: | |
| 456 WireBlock(offset, completion.offset_0_); | |
| 457 break; | |
| 458 case kBranch: | |
| 459 WireBlock(offset, completion.offset_0_); | |
| 460 WireBlock(offset, completion.offset_1_); | |
| 461 break; | |
| 462 } | |
| 463 ++offset; | |
| 464 } | |
| 465 } | |
| 466 | |
| 467 void WireBlock(size_t block_offset, int jump_offset) { | |
| 468 size_t target_block_offset = | |
| 469 block_offset + static_cast<size_t>(jump_offset); | |
| 470 CHECK(block_offset < instruction_blocks_.size()); | |
| 471 CHECK(target_block_offset < instruction_blocks_.size()); | |
| 472 auto block = instruction_blocks_[block_offset]; | |
| 473 auto target = instruction_blocks_[target_block_offset]; | |
| 474 block->successors().push_back(target->rpo_number()); | |
| 475 target->predecessors().push_back(block->rpo_number()); | |
| 476 } | |
| 477 | |
| 478 int Emit(int instruction_index, InstructionCode code, size_t outputs_size, | |
| 479 InstructionOperand** outputs, size_t inputs_size = 0, | |
| 480 InstructionOperand* *inputs = nullptr, size_t temps_size = 0, | |
| 481 InstructionOperand* *temps = nullptr) { | |
| 482 auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size, | |
| 483 inputs, temps_size, temps); | |
| 484 return AddInstruction(instruction_index, instruction); | |
| 485 } | |
| 486 | |
| 487 int AddInstruction(int instruction_index, Instruction* instruction) { | |
| 488 sequence()->AddInstruction(instruction); | |
| 489 return instruction_index; | |
| 490 } | |
| 491 | |
| 492 struct LoopData { | |
| 493 Rpo loop_header_; | |
| 494 int expected_blocks_; | |
| 495 }; | |
| 496 | |
| 497 typedef std::vector<LoopData> LoopBlocks; | |
| 498 typedef std::map<int, const Instruction*> Instructions; | |
| 499 typedef std::vector<BlockCompletion> Completions; | |
| 500 | |
| 501 SmartPointer<RegisterConfiguration> config_; | |
| 502 Frame* frame_; | |
| 503 InstructionSequence* sequence_; | |
| 504 int num_general_registers_; | |
| 505 int num_double_registers_; | |
| 506 | |
| 507 // Block building state. | |
| 508 InstructionBlocks instruction_blocks_; | |
| 509 Instructions instructions_; | |
| 510 int current_instruction_index_; | |
| 511 Completions completions_; | |
| 512 LoopBlocks loop_blocks_; | |
| 513 InstructionBlock* current_block_; | |
| 514 bool block_returns_; | |
| 515 }; | 18 }; |
| 516 | 19 |
| 517 | 20 |
| 518 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { | 21 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { |
| 519 // return p0 + p1; | 22 // return p0 + p1; |
| 520 StartBlock(); | 23 StartBlock(); |
| 521 auto a_reg = Parameter(); | 24 auto a_reg = Parameter(); |
| 522 auto b_reg = Parameter(); | 25 auto b_reg = Parameter(); |
| 523 auto c_reg = EmitOII(Reg(1), Reg(a_reg, 1), Reg(b_reg, 0)); | 26 auto c_reg = EmitOII(Reg(1), Reg(a_reg, 1), Reg(b_reg, 0)); |
| 524 Return(c_reg); | 27 Return(c_reg); |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 for (int i = 0; i < kPhis; ++i) { | 131 for (int i = 0; i < kPhis; ++i) { |
| 629 f_vals[i] = DefineConstant(); | 132 f_vals[i] = DefineConstant(); |
| 630 } | 133 } |
| 631 EndBlock(Jump(1)); | 134 EndBlock(Jump(1)); |
| 632 | 135 |
| 633 StartBlock(); | 136 StartBlock(); |
| 634 TestOperand merged[kPhis]; | 137 TestOperand merged[kPhis]; |
| 635 for (int i = 0; i < kPhis; ++i) { | 138 for (int i = 0; i < kPhis; ++i) { |
| 636 merged[i] = Use(Phi(t_vals[i], f_vals[i])); | 139 merged[i] = Use(Phi(t_vals[i], f_vals[i])); |
| 637 } | 140 } |
| 638 Return(EmitCall(Reg(), kPhis, merged)); | 141 Return(EmitCall(Slot(-1), kPhis, merged)); |
| 639 EndBlock(); | 142 EndBlock(); |
| 640 | 143 |
| 641 Allocate(); | 144 Allocate(); |
| 642 } | 145 } |
| 643 | 146 |
| 644 | 147 |
| 645 TEST_F(RegisterAllocatorTest, DoubleDiamondManyRedundantPhis) { | 148 TEST_F(RegisterAllocatorTest, DoubleDiamondManyRedundantPhis) { |
| 646 const int kPhis = kDefaultNRegs * 2; | 149 const int kPhis = kDefaultNRegs * 2; |
| 647 | 150 |
| 648 // First diamond. | 151 // First diamond. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 667 EndBlock(Jump(2)); | 170 EndBlock(Jump(2)); |
| 668 | 171 |
| 669 StartBlock(); | 172 StartBlock(); |
| 670 EndBlock(Jump(1)); | 173 EndBlock(Jump(1)); |
| 671 | 174 |
| 672 StartBlock(); | 175 StartBlock(); |
| 673 TestOperand merged[kPhis]; | 176 TestOperand merged[kPhis]; |
| 674 for (int i = 0; i < kPhis; ++i) { | 177 for (int i = 0; i < kPhis; ++i) { |
| 675 merged[i] = Use(Phi(vals[i], vals[i])); | 178 merged[i] = Use(Phi(vals[i], vals[i])); |
| 676 } | 179 } |
| 677 Return(EmitCall(Reg(), kPhis, merged)); | 180 Return(EmitCall(Reg(0), kPhis, merged)); |
| 678 EndBlock(); | 181 EndBlock(); |
| 679 | 182 |
| 680 Allocate(); | 183 Allocate(); |
| 681 } | 184 } |
| 682 | 185 |
| 683 | 186 |
| 684 TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) { | 187 TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) { |
| 685 const size_t kNumRegs = 3; | 188 const size_t kNumRegs = 3; |
| 686 const size_t kParams = kNumRegs + 1; | 189 const size_t kParams = kNumRegs + 1; |
| 687 // Override number of registers. | 190 // Override number of registers. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 722 EndLoop(); | 225 EndLoop(); |
| 723 } | 226 } |
| 724 | 227 |
| 725 StartBlock(); | 228 StartBlock(); |
| 726 Return(DefineConstant()); | 229 Return(DefineConstant()); |
| 727 EndBlock(); | 230 EndBlock(); |
| 728 | 231 |
| 729 Allocate(); | 232 Allocate(); |
| 730 } | 233 } |
| 731 | 234 |
| 235 |
| 236 TEST_F(RegisterAllocatorTest, SpillPhi) { |
| 237 StartBlock(); |
| 238 EndBlock(Branch(Imm(), 1, 2)); |
| 239 |
| 240 StartBlock(); |
| 241 auto left = Define(Reg(0)); |
| 242 EndBlock(Jump(2)); |
| 243 |
| 244 StartBlock(); |
| 245 auto right = Define(Reg(0)); |
| 246 EndBlock(); |
| 247 |
| 248 StartBlock(); |
| 249 auto phi = Phi(left, right); |
| 250 EmitCall(Slot(-1)); |
| 251 Return(Reg(phi)); |
| 252 EndBlock(); |
| 253 |
| 254 Allocate(); |
| 255 } |
| 256 |
| 257 |
| 258 TEST_F(RegisterAllocatorTest, MoveLotsOfConstants) { |
| 259 StartBlock(); |
| 260 VReg constants[kDefaultNRegs]; |
| 261 for (size_t i = 0; i < arraysize(constants); ++i) { |
| 262 constants[i] = DefineConstant(); |
| 263 } |
| 264 TestOperand call_ops[kDefaultNRegs * 2]; |
| 265 for (int i = 0; i < kDefaultNRegs; ++i) { |
| 266 call_ops[i] = Reg(constants[i], i); |
| 267 } |
| 268 for (int i = 0; i < kDefaultNRegs; ++i) { |
| 269 call_ops[i + kDefaultNRegs] = Slot(constants[i], i); |
| 270 } |
| 271 EmitCall(Slot(-1), arraysize(call_ops), call_ops); |
| 272 EndBlock(Last()); |
| 273 |
| 274 Allocate(); |
| 275 } |
| 276 |
| 732 } // namespace compiler | 277 } // namespace compiler |
| 733 } // namespace internal | 278 } // namespace internal |
| 734 } // namespace v8 | 279 } // namespace v8 |
| OLD | NEW |