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
3 // found in the LICENSE file.
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"
16 general_register_names_[RegisterConfiguration::kMaxGeneralRegisters];
18 double_register_names_[RegisterConfiguration::kMaxDoubleRegisters];
19 static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters +
20 RegisterConfiguration::kMaxDoubleRegisters)];
23 static 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);
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;
38 InstructionSequenceTest::InstructionSequenceTest()
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();
50 void 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;
60 RegisterConfiguration* 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_));
70 InstructionSequence* InstructionSequenceTest::sequence() {
71 if (sequence_ == nullptr) {
72 sequence_ = new (zone())
73 InstructionSequence(isolate(), zone(), &instruction_blocks_);
79 void InstructionSequenceTest::StartLoop(int loop_blocks) {
80 CHECK(current_block_ == nullptr);
81 if (!loop_blocks_.empty()) {
82 CHECK(!loop_blocks_.back().loop_header_.IsValid());
84 LoopData loop_data = {Rpo::Invalid(), loop_blocks};
85 loop_blocks_.push_back(loop_data);
89 void InstructionSequenceTest::EndLoop() {
90 CHECK(current_block_ == nullptr);
91 CHECK(!loop_blocks_.empty());
92 CHECK_EQ(0, loop_blocks_.back().expected_blocks_);
93 loop_blocks_.pop_back();
97 void InstructionSequenceTest::StartBlock() {
98 block_returns_ = false;
103 int InstructionSequenceTest::EndBlock(BlockCompletion completion) {
104 int instruction_index = kMinInt;
105 if (block_returns_) {
106 CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough);
107 completion.type_ = kBlockEnd;
109 switch (completion.type_) {
113 instruction_index = EmitFallThrough();
116 CHECK(!block_returns_);
117 instruction_index = EmitJump();
120 CHECK(!block_returns_);
121 instruction_index = EmitBranch(completion.op_);
124 completions_.push_back(completion);
125 CHECK(current_block_ != nullptr);
126 sequence()->EndBlock(current_block_->rpo_number());
127 current_block_ = nullptr;
128 return instruction_index;
132 InstructionSequenceTest::TestOperand InstructionSequenceTest::Imm(int32_t imm) {
133 int index = sequence()->AddImmediate(Constant(imm));
134 return TestOperand(kImmediate, index);
138 InstructionSequenceTest::VReg InstructionSequenceTest::Define(
139 TestOperand output_op) {
140 VReg vreg = NewReg();
141 InstructionOperand outputs[1]{ConvertOutputOp(vreg, output_op)};
142 Emit(vreg.value_, kArchNop, 1, outputs);
147 int InstructionSequenceTest::Return(TestOperand input_op_0) {
148 block_returns_ = true;
149 InstructionOperand inputs[1]{ConvertInputOp(input_op_0)};
150 return Emit(NewIndex(), kArchRet, 0, nullptr, 1, inputs);
154 PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
155 VReg incoming_vreg_1,
156 VReg incoming_vreg_2,
157 VReg incoming_vreg_3) {
158 VReg inputs[] = {incoming_vreg_0, incoming_vreg_1, incoming_vreg_2,
160 size_t input_count = 0;
161 for (; input_count < arraysize(inputs); ++input_count) {
162 if (inputs[input_count].value_ == kNoValue) break;
164 CHECK(input_count > 0);
165 auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, input_count);
166 for (size_t i = 0; i < input_count; ++i) {
167 SetInput(phi, i, inputs[i]);
169 current_block_->AddPhi(phi);
174 PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
175 size_t input_count) {
176 auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, input_count);
177 SetInput(phi, 0, incoming_vreg_0);
178 current_block_->AddPhi(phi);
183 void InstructionSequenceTest::SetInput(PhiInstruction* phi, size_t input,
185 CHECK(vreg.value_ != kNoValue);
186 phi->SetInput(input, vreg.value_);
190 InstructionSequenceTest::VReg InstructionSequenceTest::DefineConstant(
192 VReg vreg = NewReg();
193 sequence()->AddConstant(vreg.value_, Constant(imm));
194 InstructionOperand outputs[1]{ConstantOperand(vreg.value_)};
195 Emit(vreg.value_, kArchNop, 1, outputs);
200 int InstructionSequenceTest::EmitNop() { return Emit(NewIndex(), kArchNop); }
203 static size_t CountInputs(size_t size,
204 InstructionSequenceTest::TestOperand* inputs) {
206 for (; i < size; ++i) {
207 if (inputs[i].type_ == InstructionSequenceTest::kInvalid) break;
213 int InstructionSequenceTest::EmitI(size_t input_size, TestOperand* inputs) {
214 InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
215 return Emit(NewIndex(), kArchNop, 0, nullptr, input_size, mapped_inputs);
219 int InstructionSequenceTest::EmitI(TestOperand input_op_0,
220 TestOperand input_op_1,
221 TestOperand input_op_2,
222 TestOperand input_op_3) {
223 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
224 return EmitI(CountInputs(arraysize(inputs), inputs), inputs);
228 InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
229 TestOperand output_op, size_t input_size, TestOperand* inputs) {
230 VReg output_vreg = NewReg();
231 InstructionOperand outputs[1]{ConvertOutputOp(output_vreg, output_op)};
232 InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
233 Emit(output_vreg.value_, kArchNop, 1, outputs, input_size, mapped_inputs);
238 InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
239 TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
240 TestOperand input_op_2, TestOperand input_op_3) {
241 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
242 return EmitOI(output_op, CountInputs(arraysize(inputs), inputs), inputs);
246 InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
247 TestOperand output_op, size_t input_size, TestOperand* inputs) {
248 VReg output_vreg = NewReg();
249 InstructionOperand outputs[1]{ConvertOutputOp(output_vreg, output_op)};
250 CHECK(UnallocatedOperand::cast(outputs[0]).HasFixedPolicy());
251 InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
252 Emit(output_vreg.value_, kArchCallCodeObject, 1, outputs, input_size,
253 mapped_inputs, 0, nullptr, true);
258 InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
259 TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
260 TestOperand input_op_2, TestOperand input_op_3) {
261 TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
262 return EmitCall(output_op, CountInputs(arraysize(inputs), inputs), inputs);
266 const Instruction* InstructionSequenceTest::GetInstruction(
267 int instruction_index) {
268 auto it = instructions_.find(instruction_index);
269 CHECK(it != instructions_.end());
274 int InstructionSequenceTest::EmitBranch(TestOperand input_op) {
275 InstructionOperand inputs[4]{ConvertInputOp(input_op), ConvertInputOp(Imm()),
276 ConvertInputOp(Imm()), ConvertInputOp(Imm())};
277 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
278 FlagsConditionField::encode(kEqual);
280 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl();
281 return AddInstruction(NewIndex(), instruction);
285 int InstructionSequenceTest::EmitFallThrough() {
286 auto instruction = NewInstruction(kArchNop, 0, nullptr)->MarkAsControl();
287 return AddInstruction(NewIndex(), instruction);
291 int InstructionSequenceTest::EmitJump() {
292 InstructionOperand inputs[1]{ConvertInputOp(Imm())};
294 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl();
295 return AddInstruction(NewIndex(), instruction);
299 Instruction* InstructionSequenceTest::NewInstruction(
300 InstructionCode code, size_t outputs_size, InstructionOperand* outputs,
301 size_t inputs_size, InstructionOperand* inputs, size_t temps_size,
302 InstructionOperand* temps) {
303 CHECK(current_block_);
304 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size,
305 inputs, temps_size, temps);
309 InstructionOperand InstructionSequenceTest::Unallocated(
310 TestOperand op, UnallocatedOperand::ExtendedPolicy policy) {
311 return UnallocatedOperand(policy, op.vreg_.value_);
315 InstructionOperand InstructionSequenceTest::Unallocated(
316 TestOperand op, UnallocatedOperand::ExtendedPolicy policy,
317 UnallocatedOperand::Lifetime lifetime) {
318 return UnallocatedOperand(policy, lifetime, op.vreg_.value_);
322 InstructionOperand InstructionSequenceTest::Unallocated(
323 TestOperand op, UnallocatedOperand::ExtendedPolicy policy, int index) {
324 return UnallocatedOperand(policy, index, op.vreg_.value_);
328 InstructionOperand InstructionSequenceTest::Unallocated(
329 TestOperand op, UnallocatedOperand::BasicPolicy policy, int index) {
330 return UnallocatedOperand(policy, index, op.vreg_.value_);
334 InstructionOperand* InstructionSequenceTest::ConvertInputs(
335 size_t input_size, TestOperand* inputs) {
336 InstructionOperand* mapped_inputs =
337 zone()->NewArray<InstructionOperand>(static_cast<int>(input_size));
338 for (size_t i = 0; i < input_size; ++i) {
339 mapped_inputs[i] = ConvertInputOp(inputs[i]);
341 return mapped_inputs;
345 InstructionOperand InstructionSequenceTest::ConvertInputOp(TestOperand op) {
346 if (op.type_ == kImmediate) {
347 CHECK_EQ(op.vreg_.value_, kNoValue);
348 return ImmediateOperand(op.value_);
350 CHECK_NE(op.vreg_.value_, kNoValue);
353 return Unallocated(op, UnallocatedOperand::NONE,
354 UnallocatedOperand::USED_AT_START);
356 return Unallocated(op, UnallocatedOperand::NONE);
357 case kUniqueRegister:
358 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
360 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER,
361 UnallocatedOperand::USED_AT_START);
363 CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
364 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
366 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
371 return InstructionOperand();
375 InstructionOperand InstructionSequenceTest::ConvertOutputOp(VReg vreg,
377 CHECK_EQ(op.vreg_.value_, kNoValue);
381 return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT);
383 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
385 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
387 CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
388 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
393 return InstructionOperand();
397 InstructionBlock* InstructionSequenceTest::NewBlock() {
398 CHECK(current_block_ == nullptr);
399 auto block_id = BasicBlock::Id::FromSize(instruction_blocks_.size());
400 Rpo rpo = Rpo::FromInt(block_id.ToInt());
401 Rpo loop_header = Rpo::Invalid();
402 Rpo loop_end = Rpo::Invalid();
403 if (!loop_blocks_.empty()) {
404 auto& loop_data = loop_blocks_.back();
405 // This is a loop header.
406 if (!loop_data.loop_header_.IsValid()) {
407 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_);
408 loop_data.expected_blocks_--;
409 loop_data.loop_header_ = rpo;
411 // This is a loop body.
412 CHECK_NE(0, loop_data.expected_blocks_);
413 // TODO(dcarney): handle nested loops.
414 loop_data.expected_blocks_--;
415 loop_header = loop_data.loop_header_;
418 // Construct instruction block.
419 auto instruction_block = new (zone())
420 InstructionBlock(zone(), block_id, rpo, loop_header, loop_end, false);
421 instruction_blocks_.push_back(instruction_block);
422 current_block_ = instruction_block;
423 sequence()->StartBlock(rpo);
424 return instruction_block;
428 void InstructionSequenceTest::WireBlocks() {
429 CHECK(!current_block());
430 CHECK(instruction_blocks_.size() == completions_.size());
432 for (const auto& completion : completions_) {
433 switch (completion.type_) {
436 case kFallThrough: // Fallthrough.
438 WireBlock(offset, completion.offset_0_);
441 WireBlock(offset, completion.offset_0_);
442 WireBlock(offset, completion.offset_1_);
450 void InstructionSequenceTest::WireBlock(size_t block_offset, int jump_offset) {
451 size_t target_block_offset = block_offset + static_cast<size_t>(jump_offset);
452 CHECK(block_offset < instruction_blocks_.size());
453 CHECK(target_block_offset < instruction_blocks_.size());
454 auto block = instruction_blocks_[block_offset];
455 auto target = instruction_blocks_[target_block_offset];
456 block->successors().push_back(target->rpo_number());
457 target->predecessors().push_back(block->rpo_number());
461 int InstructionSequenceTest::Emit(int instruction_index, InstructionCode code,
463 InstructionOperand* outputs,
465 InstructionOperand* inputs, size_t temps_size,
466 InstructionOperand* temps, bool is_call) {
467 auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size,
468 inputs, temps_size, temps);
469 if (is_call) instruction->MarkAsCall();
470 return AddInstruction(instruction_index, instruction);
474 int InstructionSequenceTest::AddInstruction(int instruction_index,
475 Instruction* instruction) {
476 sequence()->AddInstruction(instruction);
477 return instruction_index;
480 } // namespace compiler
481 } // namespace internal