Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(485)

Side by Side Diff: test/unittests/compiler/register-allocator-unittest.cc

Issue 699083003: [turbofan] extend register allocator testing with control flow (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « test/cctest/compiler/test-instruction.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "test/unittests/test-utils.h" 7 #include "test/unittests/test-utils.h"
8 #include "testing/gmock/include/gmock/gmock.h" 8 #include "testing/gmock/include/gmock/gmock.h"
9 9
10 namespace v8 { 10 namespace v8 {
(...skipping 19 matching lines...) Expand all
30 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); 30 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i);
31 *loc++ = 0; 31 *loc++ = 0;
32 } 32 }
33 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { 33 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) {
34 double_register_names_[i] = loc; 34 double_register_names_[i] = loc;
35 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; 35 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1;
36 *loc++ = 0; 36 *loc++ = 0;
37 } 37 }
38 } 38 }
39 39
40 enum BlockCompletionType { kFallThrough, kBranch, kJump };
41
42 struct BlockCompletion {
43 BlockCompletionType type_;
44 int vreg_;
45 int offset_0_;
46 int offset_1_;
47 };
48
49 static const int kInvalidJumpOffset = kMinInt;
50
51 BlockCompletion FallThrough() {
52 BlockCompletion completion = {kFallThrough, -1, 1, kInvalidJumpOffset};
53 return completion;
54 }
55
56
57 BlockCompletion Jump(int offset) {
58 BlockCompletion completion = {kJump, -1, offset, kInvalidJumpOffset};
59 return completion;
60 }
61
62
63 BlockCompletion Branch(int vreg, int left_offset, int right_offset) {
64 BlockCompletion completion = {kBranch, vreg, left_offset, right_offset};
65 return completion;
66 }
67
40 } // namespace 68 } // namespace
41 69
42 70
43 // TODO(dcarney): fake opcodes.
44 // TODO(dcarney): fix printing of sequence w.r.t fake opcodes and registers.
45 class RegisterAllocatorTest : public TestWithZone { 71 class RegisterAllocatorTest : public TestWithZone {
46 public: 72 public:
47 static const int kDefaultNRegs = 4; 73 static const int kDefaultNRegs = 4;
48 74
49 RegisterAllocatorTest() 75 RegisterAllocatorTest()
50 : num_general_registers_(kDefaultNRegs), 76 : num_general_registers_(kDefaultNRegs),
51 num_double_registers_(kDefaultNRegs), 77 num_double_registers_(kDefaultNRegs),
52 basic_blocks_(zone()),
53 instruction_blocks_(zone()), 78 instruction_blocks_(zone()),
54 current_block_(NULL) { 79 current_block_(nullptr),
80 is_last_block_(false) {
55 InitializeRegisterNames(); 81 InitializeRegisterNames();
56 } 82 }
57 83
84 void SetNumRegs(int num_general_registers, int num_double_registers) {
85 CHECK(instruction_blocks_.empty());
86 num_general_registers_ = num_general_registers;
87 num_double_registers_ = num_double_registers;
88 }
89
58 RegisterConfiguration* config() { 90 RegisterConfiguration* config() {
59 if (config_.is_empty()) { 91 if (config_.is_empty()) {
60 config_.Reset(new RegisterConfiguration( 92 config_.Reset(new RegisterConfiguration(
61 num_general_registers_, num_double_registers_, num_double_registers_, 93 num_general_registers_, num_double_registers_, num_double_registers_,
62 general_register_names_, double_register_names_)); 94 general_register_names_, double_register_names_));
63 } 95 }
64 return config_.get(); 96 return config_.get();
65 } 97 }
66 98
67 Frame* frame() { 99 Frame* frame() {
(...skipping 11 matching lines...) Expand all
79 } 111 }
80 112
81 RegisterAllocator* allocator() { 113 RegisterAllocator* allocator() {
82 if (allocator_.is_empty()) { 114 if (allocator_.is_empty()) {
83 allocator_.Reset( 115 allocator_.Reset(
84 new RegisterAllocator(config(), zone(), frame(), sequence())); 116 new RegisterAllocator(config(), zone(), frame(), sequence()));
85 } 117 }
86 return allocator_.get(); 118 return allocator_.get();
87 } 119 }
88 120
89 InstructionBlock* StartBlock(Rpo loop_header = Rpo::Invalid(), 121 void StartLoop(int loop_blocks) {
90 Rpo loop_end = Rpo::Invalid()) { 122 CHECK(current_block_ == nullptr);
91 CHECK(current_block_ == NULL); 123 if (!loop_blocks_.empty()) {
92 BasicBlock::Id block_id = 124 CHECK(!loop_blocks_.back().loop_header_.IsValid());
93 BasicBlock::Id::FromSize(instruction_blocks_.size());
94 BasicBlock* basic_block = new (zone()) BasicBlock(zone(), block_id);
95 basic_block->set_rpo_number(block_id.ToInt());
96 basic_block->set_ao_number(block_id.ToInt());
97 if (loop_header.IsValid()) {
98 basic_block->set_loop_depth(1);
99 basic_block->set_loop_header(basic_blocks_[loop_header.ToSize()]);
100 basic_block->set_loop_end(basic_blocks_[loop_end.ToSize()]);
101 } 125 }
102 InstructionBlock* instruction_block = 126 LoopData loop_data = {Rpo::Invalid(), loop_blocks};
103 new (zone()) InstructionBlock(zone(), basic_block); 127 loop_blocks_.push_back(loop_data);
104 basic_blocks_.push_back(basic_block);
105 instruction_blocks_.push_back(instruction_block);
106 current_block_ = instruction_block;
107 sequence()->StartBlock(basic_block);
108 return instruction_block;
109 } 128 }
110 129
111 void EndBlock() { 130 void EndLoop() {
112 CHECK(current_block_ != NULL); 131 CHECK(current_block_ == nullptr);
113 sequence()->EndBlock(basic_blocks_[current_block_->rpo_number().ToSize()]); 132 CHECK(!loop_blocks_.empty());
114 current_block_ = NULL; 133 CHECK_EQ(0, loop_blocks_.back().expected_blocks_);
134 loop_blocks_.pop_back();
135 }
136
137 void StartLastBlock() {
138 CHECK(!is_last_block_);
139 is_last_block_ = true;
140 NewBlock();
141 }
142
143 void StartBlock() {
144 CHECK(!is_last_block_);
145 NewBlock();
146 }
147
148 void EndBlock(BlockCompletion completion = FallThrough()) {
149 completions_.push_back(completion);
150 switch (completion.type_) {
151 case kFallThrough:
152 if (is_last_block_) break;
153 // TODO(dcarney): we don't emit this after returns.
154 EmitFallThrough();
155 break;
156 case kJump:
157 EmitJump();
158 break;
159 case kBranch:
160 EmitBranch(completion.vreg_);
161 break;
162 }
163 CHECK(current_block_ != nullptr);
164 sequence()->EndBlock(current_block_->rpo_number());
165 current_block_ = nullptr;
115 } 166 }
116 167
117 void Allocate() { 168 void Allocate() {
118 if (FLAG_trace_alloc) { 169 CHECK_EQ(nullptr, current_block_);
170 CHECK(is_last_block_);
171 WireBlocks();
172 if (FLAG_trace_alloc || FLAG_trace_turbo) {
119 OFStream os(stdout); 173 OFStream os(stdout);
120 PrintableInstructionSequence printable = {config(), sequence()}; 174 PrintableInstructionSequence printable = {config(), sequence()};
121 os << "Before: " << std::endl << printable << std::endl; 175 os << "Before: " << std::endl << printable << std::endl;
122 } 176 }
123 allocator()->Allocate(); 177 allocator()->Allocate();
124 if (FLAG_trace_alloc) { 178 if (FLAG_trace_alloc || FLAG_trace_turbo) {
125 OFStream os(stdout); 179 OFStream os(stdout);
126 PrintableInstructionSequence printable = {config(), sequence()}; 180 PrintableInstructionSequence printable = {config(), sequence()};
127 os << "After: " << std::endl << printable << std::endl; 181 os << "After: " << std::endl << printable << std::endl;
128 } 182 }
129 } 183 }
130 184
131 int NewReg() { return sequence()->NextVirtualRegister(); } 185 int NewReg() { return sequence()->NextVirtualRegister(); }
132 186
133 int Parameter() { 187 int Parameter() {
134 // TODO(dcarney): assert parameters before other instructions.
135 int vreg = NewReg(); 188 int vreg = NewReg();
189 InstructionOperand* outputs[1]{UseRegister(vreg)};
190 Emit(kArchNop, 1, outputs);
191 return vreg;
192 }
193
194 Instruction* Return(int vreg) {
195 InstructionOperand* inputs[1]{UseRegister(vreg)};
196 return Emit(kArchRet, 0, nullptr, 1, inputs);
197 }
198
199 PhiInstruction* Phi(int vreg) {
200 PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg());
201 phi->operands().push_back(vreg);
202 current_block_->AddPhi(phi);
203 return phi;
204 }
205
206 int DefineConstant(int32_t imm = 0) {
207 int virtual_register = NewReg();
208 sequence()->AddConstant(virtual_register, Constant(imm));
136 InstructionOperand* outputs[1]{ 209 InstructionOperand* outputs[1]{
137 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)}; 210 ConstantOperand::Create(virtual_register, zone())};
138 sequence()->AddInstruction( 211 Emit(kArchNop, 1, outputs);
139 Instruction::New(zone(), kArchNop, 1, outputs, 0, NULL, 0, NULL)); 212 return virtual_register;
140 return vreg; 213 }
141 } 214
142 215 ImmediateOperand* Immediate(int32_t imm = 0) {
143 void Return(int vreg) { 216 int index = sequence()->AddImmediate(Constant(imm));
144 InstructionOperand* inputs[1]{ 217 return ImmediateOperand::Create(index, zone());
145 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)}; 218 }
146 sequence()->AddInstruction( 219
147 Instruction::New(zone(), kArchNop, 0, NULL, 1, inputs, 0, NULL)); 220 Instruction* EmitFRI(int output_vreg, int input_vreg_0) {
148 } 221 InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)};
149 222 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Immediate()};
150 Instruction* Emit(int output_vreg, int input_vreg_0, int input_vreg_1) { 223 return Emit(kArchNop, 1, outputs, 2, inputs);
151 InstructionOperand* outputs[1]{ 224 }
152 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, output_vreg)}; 225
153 InstructionOperand* inputs[2]{ 226 Instruction* EmitFRU(int output_vreg, int input_vreg_0, int input_vreg_1) {
154 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, input_vreg_0), 227 InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)};
155 Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, input_vreg_1)}; 228 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Use(input_vreg_1)};
156 Instruction* instruction = 229 return Emit(kArchNop, 1, outputs, 2, inputs);
157 Instruction::New(zone(), kArchNop, 1, outputs, 2, inputs, 0, NULL); 230 }
158 sequence()->AddInstruction(instruction); 231
159 return instruction; 232 Instruction* EmitRRR(int output_vreg, int input_vreg_0, int input_vreg_1) {
233 InstructionOperand* outputs[1]{UseRegister(output_vreg)};
234 InstructionOperand* inputs[2]{UseRegister(input_vreg_0),
235 UseRegister(input_vreg_1)};
236 return Emit(kArchNop, 1, outputs, 2, inputs);
160 } 237 }
161 238
162 private: 239 private:
163 InstructionOperand* Unallocated(UnallocatedOperand::ExtendedPolicy policy, 240 InstructionOperand* Unallocated(int vreg,
164 int vreg) { 241 UnallocatedOperand::ExtendedPolicy policy) {
165 UnallocatedOperand* op = 242 UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy);
166 new (zone()) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER);
167 op->set_virtual_register(vreg); 243 op->set_virtual_register(vreg);
168 return op; 244 return op;
169 } 245 }
170 246
171 int num_general_registers_; 247 InstructionOperand* Unallocated(int vreg,
172 int num_double_registers_; 248 UnallocatedOperand::ExtendedPolicy policy,
249 UnallocatedOperand::Lifetime lifetime) {
250 UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy, lifetime);
251 op->set_virtual_register(vreg);
252 return op;
253 }
254
255 InstructionOperand* UseRegister(int vreg) {
256 return Unallocated(vreg, UnallocatedOperand::MUST_HAVE_REGISTER);
257 }
258
259 InstructionOperand* DefineSameAsFirst(int vreg) {
260 return Unallocated(vreg, UnallocatedOperand::SAME_AS_FIRST_INPUT);
261 }
262
263 InstructionOperand* Use(int vreg) {
264 return Unallocated(vreg, UnallocatedOperand::NONE,
265 UnallocatedOperand::USED_AT_START);
266 }
267
268 void EmitBranch(int vreg) {
269 InstructionOperand* inputs[4]{UseRegister(vreg), Immediate(), Immediate(),
270 Immediate()};
271 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
272 FlagsConditionField::encode(kEqual);
273 Instruction* instruction =
274 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl();
275 sequence()->AddInstruction(instruction);
276 }
277
278 void EmitFallThrough() {
279 Instruction* instruction =
280 NewInstruction(kArchNop, 0, nullptr)->MarkAsControl();
281 sequence()->AddInstruction(instruction);
282 }
283
284 void EmitJump() {
285 InstructionOperand* inputs[1]{Immediate()};
286 Instruction* instruction =
287 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl();
288 sequence()->AddInstruction(instruction);
289 }
290
291 Instruction* NewInstruction(InstructionCode code, size_t outputs_size,
292 InstructionOperand** outputs,
293 size_t inputs_size = 0,
294 InstructionOperand* *inputs = nullptr,
295 size_t temps_size = 0,
296 InstructionOperand* *temps = nullptr) {
297 CHECK_NE(nullptr, current_block_);
298 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size,
299 inputs, temps_size, temps);
300 }
301
302 Instruction* Emit(InstructionCode code, size_t outputs_size,
303 InstructionOperand** outputs, size_t inputs_size = 0,
304 InstructionOperand* *inputs = nullptr,
305 size_t temps_size = 0,
306 InstructionOperand* *temps = nullptr) {
307 Instruction* instruction = NewInstruction(
308 code, outputs_size, outputs, inputs_size, inputs, temps_size, temps);
309 sequence()->AddInstruction(instruction);
310 return instruction;
311 }
312
313 InstructionBlock* NewBlock() {
314 CHECK(current_block_ == nullptr);
315 BasicBlock::Id block_id =
316 BasicBlock::Id::FromSize(instruction_blocks_.size());
317 Rpo rpo = Rpo::FromInt(block_id.ToInt());
318 Rpo loop_header = Rpo::Invalid();
319 Rpo loop_end = Rpo::Invalid();
320 if (!loop_blocks_.empty()) {
321 auto& loop_data = loop_blocks_.back();
322 // This is a loop header.
323 if (!loop_data.loop_header_.IsValid()) {
324 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_);
325 loop_data.expected_blocks_--;
326 loop_data.loop_header_ = rpo;
327 } else {
328 // This is a loop body.
329 CHECK_NE(0, loop_data.expected_blocks_);
330 // TODO(dcarney): handle nested loops.
331 loop_data.expected_blocks_--;
332 loop_header = loop_data.loop_header_;
333 }
334 }
335 // Construct instruction block.
336 InstructionBlock* instruction_block = new (zone()) InstructionBlock(
337 zone(), block_id, rpo, rpo, loop_header, loop_end, false);
338 instruction_blocks_.push_back(instruction_block);
339 current_block_ = instruction_block;
340 sequence()->StartBlock(rpo);
341 return instruction_block;
342 }
343
344 void WireBlocks() {
345 CHECK(instruction_blocks_.size() == completions_.size());
346 size_t offset = 0;
347 size_t size = instruction_blocks_.size();
348 for (const auto& completion : completions_) {
349 switch (completion.type_) {
350 case kFallThrough:
351 if (offset == size - 1) break;
352 // Fallthrough.
353 case kJump:
354 WireBlock(offset, completion.offset_0_);
355 break;
356 case kBranch:
357 WireBlock(offset, completion.offset_0_);
358 WireBlock(offset, completion.offset_1_);
359 break;
360 }
361 ++offset;
362 }
363 }
364
365 void WireBlock(size_t block_offset, int jump_offset) {
366 size_t target_block_offset =
367 block_offset + static_cast<size_t>(jump_offset);
368 CHECK(block_offset < instruction_blocks_.size());
369 CHECK(target_block_offset < instruction_blocks_.size());
370 InstructionBlock* block = instruction_blocks_[block_offset];
371 InstructionBlock* target = instruction_blocks_[target_block_offset];
372 block->successors().push_back(target->rpo_number());
373 target->predecessors().push_back(block->rpo_number());
374 }
375
376 struct LoopData {
377 Rpo loop_header_;
378 int expected_blocks_;
379 };
380 typedef std::vector<LoopData> LoopBlocks;
381 typedef std::vector<BlockCompletion> Completions;
382
173 SmartPointer<RegisterConfiguration> config_; 383 SmartPointer<RegisterConfiguration> config_;
174 ZoneVector<BasicBlock*> basic_blocks_;
175 InstructionBlocks instruction_blocks_;
176 InstructionBlock* current_block_;
177 SmartPointer<Frame> frame_; 384 SmartPointer<Frame> frame_;
178 SmartPointer<RegisterAllocator> allocator_; 385 SmartPointer<RegisterAllocator> allocator_;
179 SmartPointer<InstructionSequence> sequence_; 386 SmartPointer<InstructionSequence> sequence_;
387 int num_general_registers_;
388 int num_double_registers_;
389
390 // Block building state.
391 InstructionBlocks instruction_blocks_;
392 Completions completions_;
393 LoopBlocks loop_blocks_;
394 InstructionBlock* current_block_;
395 bool is_last_block_;
180 }; 396 };
181 397
182 398
183 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { 399 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) {
184 StartBlock(); 400 StartLastBlock();
185 int a_reg = Parameter(); 401 int a_reg = Parameter();
186 int b_reg = Parameter(); 402 int b_reg = Parameter();
187 int c_reg = NewReg(); 403 int c_reg = NewReg();
188 Instruction* res = Emit(c_reg, a_reg, b_reg); 404 Instruction* res = EmitRRR(c_reg, a_reg, b_reg);
189 Return(c_reg); 405 Return(c_reg);
190 EndBlock(); 406 EndBlock();
191 407
192 Allocate(); 408 Allocate();
193 409
194 ASSERT_TRUE(res->OutputAt(0)->IsRegister()); 410 ASSERT_TRUE(res->OutputAt(0)->IsRegister());
195 } 411 }
196 412
413
414 TEST_F(RegisterAllocatorTest, SimpleLoop) {
415 // i = K;
416 // while(true) { i++ }
417
418 StartBlock();
419 int i_reg = DefineConstant();
420 EndBlock();
421
422 {
423 StartLoop(1);
424
425 StartLastBlock();
426 PhiInstruction* phi = Phi(i_reg);
427 int ipp = NewReg();
428 EmitFRU(ipp, phi->virtual_register(), DefineConstant());
429 phi->operands().push_back(ipp);
430 EndBlock(Jump(0));
431
432 EndLoop();
433 }
434
435 Allocate();
436 }
437
438
439 TEST_F(RegisterAllocatorTest, SimpleBranch) {
440 // return i ? K1 : K2
441 StartBlock();
442 int i_reg = DefineConstant();
443 EndBlock(Branch(i_reg, 1, 2));
444
445 StartBlock();
446 Return(DefineConstant());
447 EndBlock();
448
449 StartLastBlock();
450 Return(DefineConstant());
451 EndBlock();
452
453 Allocate();
454 }
455
197 } // namespace compiler 456 } // namespace compiler
198 } // namespace internal 457 } // namespace internal
199 } // namespace v8 458 } // namespace v8
OLDNEW
« no previous file with comments | « test/cctest/compiler/test-instruction.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698