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 |