blob: 9546376ee8ad250dd31a73e1bf21866818584461 [file] [log] [blame]
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001// 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
3// found in the LICENSE file.
4
5#include "src/base/utils/random-number-generator.h"
6#include "src/compiler/pipeline.h"
7#include "test/unittests/compiler/instruction-sequence-unittest.h"
8#include "test/unittests/test-utils.h"
9#include "testing/gmock/include/gmock/gmock.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15static const char*
16 general_register_names_[RegisterConfiguration::kMaxGeneralRegisters];
17static const char*
18 double_register_names_[RegisterConfiguration::kMaxDoubleRegisters];
19static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters +
20 RegisterConfiguration::kMaxDoubleRegisters)];
21
22
23static void InitializeRegisterNames() {
24 char* loc = register_names_;
25 for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) {
26 general_register_names_[i] = loc;
27 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i);
28 *loc++ = 0;
29 }
30 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) {
31 double_register_names_[i] = loc;
32 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1;
33 *loc++ = 0;
34 }
35}
36
37
38InstructionSequenceTest::InstructionSequenceTest()
39 : sequence_(nullptr),
40 num_general_registers_(kDefaultNRegs),
41 num_double_registers_(kDefaultNRegs),
42 instruction_blocks_(zone()),
43 current_instruction_index_(-1),
44 current_block_(nullptr),
45 block_returns_(false) {
46 InitializeRegisterNames();
47}
48
49
50void InstructionSequenceTest::SetNumRegs(int num_general_registers,
51 int num_double_registers) {
52 CHECK(config_.is_empty());
53 CHECK(instructions_.empty());
54 CHECK(instruction_blocks_.empty());
55 num_general_registers_ = num_general_registers;
56 num_double_registers_ = num_double_registers;
57}
58
59
60RegisterConfiguration* InstructionSequenceTest::config() {
61 if (config_.is_empty()) {
62 config_.Reset(new RegisterConfiguration(
63 num_general_registers_, num_double_registers_, num_double_registers_,
64 general_register_names_, double_register_names_));
65 }
66 return config_.get();
67}
68
69
70InstructionSequence* InstructionSequenceTest::sequence() {
71 if (sequence_ == nullptr) {
72 sequence_ = new (zone()) InstructionSequence(zone(), &instruction_blocks_);
73 }
74 return sequence_;
75}
76
77
78void InstructionSequenceTest::StartLoop(int loop_blocks) {
79 CHECK(current_block_ == nullptr);
80 if (!loop_blocks_.empty()) {
81 CHECK(!loop_blocks_.back().loop_header_.IsValid());
82 }
83 LoopData loop_data = {Rpo::Invalid(), loop_blocks};
84 loop_blocks_.push_back(loop_data);
85}
86
87
88void InstructionSequenceTest::EndLoop() {
89 CHECK(current_block_ == nullptr);
90 CHECK(!loop_blocks_.empty());
91 CHECK_EQ(0, loop_blocks_.back().expected_blocks_);
92 loop_blocks_.pop_back();
93}
94
95
96void InstructionSequenceTest::StartBlock() {
97 block_returns_ = false;
98 NewBlock();
99}
100
101
102int InstructionSequenceTest::EndBlock(BlockCompletion completion) {
103 int instruction_index = kMinInt;
104 if (block_returns_) {
105 CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough);
106 completion.type_ = kBlockEnd;
107 }
108 switch (completion.type_) {
109 case kBlockEnd:
110 break;
111 case kFallThrough:
112 instruction_index = EmitFallThrough();
113 break;
114 case kJump:
115 CHECK(!block_returns_);
116 instruction_index = EmitJump();
117 break;
118 case kBranch:
119 CHECK(!block_returns_);
120 instruction_index = EmitBranch(completion.op_);
121 break;
122 }
123 completions_.push_back(completion);
124 CHECK(current_block_ != nullptr);
125 sequence()->EndBlock(current_block_->rpo_number());
126 current_block_ = nullptr;
127 return instruction_index;
128}
129
130
131InstructionSequenceTest::TestOperand InstructionSequenceTest::Imm(int32_t imm) {
132 int index = sequence()->AddImmediate(Constant(imm));
133 return TestOperand(kImmediate, index);
134}
135
136
137InstructionSequenceTest::VReg InstructionSequenceTest::Define(
138 TestOperand output_op) {
139 VReg vreg = NewReg();
140 InstructionOperand* outputs[1]{ConvertOutputOp(vreg, output_op)};
141 Emit(vreg.value_, kArchNop, 1, outputs);
142 return vreg;
143}
144
145
146int InstructionSequenceTest::Return(TestOperand input_op_0) {
147 block_returns_ = true;
148 InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)};
149 return Emit(NewIndex(), kArchRet, 0, nullptr, 1, inputs);
150}
151
152
153PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
154 VReg incoming_vreg_1,
155 VReg incoming_vreg_2,
156 VReg incoming_vreg_3) {
157 auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, 10);
158 VReg inputs[] = {incoming_vreg_0, incoming_vreg_1, incoming_vreg_2,
159 incoming_vreg_3};
160 for (size_t i = 0; i < arraysize(inputs); ++i) {
161 if (inputs[i].value_ == kNoValue) break;
162 Extend(phi, inputs[i]);
163 }
164 current_block_->AddPhi(phi);
165 return phi;
166}
167
168
169void InstructionSequenceTest::Extend(PhiInstruction* phi, VReg vreg) {
170 phi->Extend(zone(), vreg.value_);
171}
172
173
174InstructionSequenceTest::VReg InstructionSequenceTest::DefineConstant(
175 int32_t imm) {
176 VReg vreg = NewReg();
177 sequence()->AddConstant(vreg.value_, Constant(imm));
178 InstructionOperand* outputs[1]{ConstantOperand::Create(vreg.value_, zone())};
179 Emit(vreg.value_, kArchNop, 1, outputs);
180 return vreg;
181}
182
183
184int InstructionSequenceTest::EmitNop() { return Emit(NewIndex(), kArchNop); }
185
186
187static size_t CountInputs(size_t size,
188 InstructionSequenceTest::TestOperand* inputs) {
189 size_t i = 0;
190 for (; i < size; ++i) {
191 if (inputs[i].type_ == InstructionSequenceTest::kInvalid) break;
192 }
193 return i;
194}
195
196
197int InstructionSequenceTest::EmitI(size_t input_size, TestOperand* inputs) {
198 InstructionOperand** mapped_inputs = ConvertInputs(input_size, inputs);
199 return Emit(NewIndex(), kArchNop, 0, nullptr, input_size, mapped_inputs);
200}
201
202
203int InstructionSequenceTest::EmitI(TestOperand input_op_0,
204 TestOperand input_op_1,
205 TestOperand input_op_2,
206 TestOperand input_op_3) {
207 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
208 return EmitI(CountInputs(arraysize(inputs), inputs), inputs);
209}
210
211
212InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
213 TestOperand output_op, size_t input_size, TestOperand* inputs) {
214 VReg output_vreg = NewReg();
215 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)};
216 InstructionOperand** mapped_inputs = ConvertInputs(input_size, inputs);
217 Emit(output_vreg.value_, kArchNop, 1, outputs, input_size, mapped_inputs);
218 return output_vreg;
219}
220
221
222InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
223 TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
224 TestOperand input_op_2, TestOperand input_op_3) {
225 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
226 return EmitOI(output_op, CountInputs(arraysize(inputs), inputs), inputs);
227}
228
229
230InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
231 TestOperand output_op, size_t input_size, TestOperand* inputs) {
232 VReg output_vreg = NewReg();
233 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)};
234 CHECK(UnallocatedOperand::cast(outputs[0])->HasFixedPolicy());
235 InstructionOperand** mapped_inputs = ConvertInputs(input_size, inputs);
236 Emit(output_vreg.value_, kArchCallCodeObject, 1, outputs, input_size,
237 mapped_inputs, 0, nullptr, true);
238 return output_vreg;
239}
240
241
242InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
243 TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
244 TestOperand input_op_2, TestOperand input_op_3) {
245 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
246 return EmitCall(output_op, CountInputs(arraysize(inputs), inputs), inputs);
247}
248
249
250const Instruction* InstructionSequenceTest::GetInstruction(
251 int instruction_index) {
252 auto it = instructions_.find(instruction_index);
253 CHECK(it != instructions_.end());
254 return it->second;
255}
256
257
258int InstructionSequenceTest::EmitBranch(TestOperand input_op) {
259 InstructionOperand* inputs[4]{ConvertInputOp(input_op), ConvertInputOp(Imm()),
260 ConvertInputOp(Imm()), ConvertInputOp(Imm())};
261 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
262 FlagsConditionField::encode(kEqual);
263 auto instruction =
264 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl();
265 return AddInstruction(NewIndex(), instruction);
266}
267
268
269int InstructionSequenceTest::EmitFallThrough() {
270 auto instruction = NewInstruction(kArchNop, 0, nullptr)->MarkAsControl();
271 return AddInstruction(NewIndex(), instruction);
272}
273
274
275int InstructionSequenceTest::EmitJump() {
276 InstructionOperand* inputs[1]{ConvertInputOp(Imm())};
277 auto instruction =
278 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl();
279 return AddInstruction(NewIndex(), instruction);
280}
281
282
283Instruction* InstructionSequenceTest::NewInstruction(
284 InstructionCode code, size_t outputs_size, InstructionOperand** outputs,
285 size_t inputs_size, InstructionOperand** inputs, size_t temps_size,
286 InstructionOperand** temps) {
287 CHECK_NE(nullptr, current_block_);
288 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size,
289 inputs, temps_size, temps);
290}
291
292
293InstructionOperand* InstructionSequenceTest::Unallocated(
294 TestOperand op, UnallocatedOperand::ExtendedPolicy policy) {
295 auto unallocated = new (zone()) UnallocatedOperand(policy);
296 unallocated->set_virtual_register(op.vreg_.value_);
297 return unallocated;
298}
299
300
301InstructionOperand* InstructionSequenceTest::Unallocated(
302 TestOperand op, UnallocatedOperand::ExtendedPolicy policy,
303 UnallocatedOperand::Lifetime lifetime) {
304 auto unallocated = new (zone()) UnallocatedOperand(policy, lifetime);
305 unallocated->set_virtual_register(op.vreg_.value_);
306 return unallocated;
307}
308
309
310InstructionOperand* InstructionSequenceTest::Unallocated(
311 TestOperand op, UnallocatedOperand::ExtendedPolicy policy, int index) {
312 auto unallocated = new (zone()) UnallocatedOperand(policy, index);
313 unallocated->set_virtual_register(op.vreg_.value_);
314 return unallocated;
315}
316
317
318InstructionOperand* InstructionSequenceTest::Unallocated(
319 TestOperand op, UnallocatedOperand::BasicPolicy policy, int index) {
320 auto unallocated = new (zone()) UnallocatedOperand(policy, index);
321 unallocated->set_virtual_register(op.vreg_.value_);
322 return unallocated;
323}
324
325
326InstructionOperand** InstructionSequenceTest::ConvertInputs(
327 size_t input_size, TestOperand* inputs) {
328 InstructionOperand** mapped_inputs =
329 zone()->NewArray<InstructionOperand*>(static_cast<int>(input_size));
330 for (size_t i = 0; i < input_size; ++i) {
331 mapped_inputs[i] = ConvertInputOp(inputs[i]);
332 }
333 return mapped_inputs;
334}
335
336
337InstructionOperand* InstructionSequenceTest::ConvertInputOp(TestOperand op) {
338 if (op.type_ == kImmediate) {
339 CHECK_EQ(op.vreg_.value_, kNoValue);
340 return ImmediateOperand::Create(op.value_, zone());
341 }
342 CHECK_NE(op.vreg_.value_, kNoValue);
343 switch (op.type_) {
344 case kNone:
345 return Unallocated(op, UnallocatedOperand::NONE,
346 UnallocatedOperand::USED_AT_START);
347 case kUnique:
348 return Unallocated(op, UnallocatedOperand::NONE);
349 case kUniqueRegister:
350 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
351 case kRegister:
352 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER,
353 UnallocatedOperand::USED_AT_START);
354 case kFixedRegister:
355 CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
356 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
357 case kFixedSlot:
358 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
359 default:
360 break;
361 }
362 CHECK(false);
363 return NULL;
364}
365
366
367InstructionOperand* InstructionSequenceTest::ConvertOutputOp(VReg vreg,
368 TestOperand op) {
369 CHECK_EQ(op.vreg_.value_, kNoValue);
370 op.vreg_ = vreg;
371 switch (op.type_) {
372 case kSameAsFirst:
373 return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT);
374 case kRegister:
375 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
376 case kFixedSlot:
377 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
378 case kFixedRegister:
379 CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
380 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
381 default:
382 break;
383 }
384 CHECK(false);
385 return NULL;
386}
387
388
389InstructionBlock* InstructionSequenceTest::NewBlock() {
390 CHECK(current_block_ == nullptr);
391 auto block_id = BasicBlock::Id::FromSize(instruction_blocks_.size());
392 Rpo rpo = Rpo::FromInt(block_id.ToInt());
393 Rpo loop_header = Rpo::Invalid();
394 Rpo loop_end = Rpo::Invalid();
395 if (!loop_blocks_.empty()) {
396 auto& loop_data = loop_blocks_.back();
397 // This is a loop header.
398 if (!loop_data.loop_header_.IsValid()) {
399 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_);
400 loop_data.expected_blocks_--;
401 loop_data.loop_header_ = rpo;
402 } else {
403 // This is a loop body.
404 CHECK_NE(0, loop_data.expected_blocks_);
405 // TODO(dcarney): handle nested loops.
406 loop_data.expected_blocks_--;
407 loop_header = loop_data.loop_header_;
408 }
409 }
410 // Construct instruction block.
411 auto instruction_block = new (zone())
412 InstructionBlock(zone(), block_id, rpo, loop_header, loop_end, false);
413 instruction_blocks_.push_back(instruction_block);
414 current_block_ = instruction_block;
415 sequence()->StartBlock(rpo);
416 return instruction_block;
417}
418
419
420void InstructionSequenceTest::WireBlocks() {
421 CHECK_EQ(nullptr, current_block());
422 CHECK(instruction_blocks_.size() == completions_.size());
423 size_t offset = 0;
424 for (const auto& completion : completions_) {
425 switch (completion.type_) {
426 case kBlockEnd:
427 break;
428 case kFallThrough: // Fallthrough.
429 case kJump:
430 WireBlock(offset, completion.offset_0_);
431 break;
432 case kBranch:
433 WireBlock(offset, completion.offset_0_);
434 WireBlock(offset, completion.offset_1_);
435 break;
436 }
437 ++offset;
438 }
439}
440
441
442void InstructionSequenceTest::WireBlock(size_t block_offset, int jump_offset) {
443 size_t target_block_offset = block_offset + static_cast<size_t>(jump_offset);
444 CHECK(block_offset < instruction_blocks_.size());
445 CHECK(target_block_offset < instruction_blocks_.size());
446 auto block = instruction_blocks_[block_offset];
447 auto target = instruction_blocks_[target_block_offset];
448 block->successors().push_back(target->rpo_number());
449 target->predecessors().push_back(block->rpo_number());
450}
451
452
453int InstructionSequenceTest::Emit(int instruction_index, InstructionCode code,
454 size_t outputs_size,
455 InstructionOperand** outputs,
456 size_t inputs_size,
457 InstructionOperand** inputs,
458 size_t temps_size, InstructionOperand** temps,
459 bool is_call) {
460 auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size,
461 inputs, temps_size, temps);
462 if (is_call) instruction->MarkAsCall();
463 return AddInstruction(instruction_index, instruction);
464}
465
466
467int InstructionSequenceTest::AddInstruction(int instruction_index,
468 Instruction* instruction) {
469 sequence()->AddInstruction(instruction);
470 return instruction_index;
471}
472
473} // namespace compiler
474} // namespace internal
475} // namespace v8