2014-11-27 09:19:31 +00:00
|
|
|
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "src/base/utils/random-number-generator.h"
|
|
|
|
#include "src/compiler/pipeline.h"
|
|
|
|
#include "test/unittests/compiler/instruction-sequence-unittest.h"
|
|
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace compiler {
|
|
|
|
|
|
|
|
static const char*
|
|
|
|
general_register_names_[RegisterConfiguration::kMaxGeneralRegisters];
|
|
|
|
static const char*
|
2016-04-25 20:10:32 +00:00
|
|
|
double_register_names_[RegisterConfiguration::kMaxFPRegisters];
|
2014-11-27 09:19:31 +00:00
|
|
|
static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters +
|
2016-04-25 20:10:32 +00:00
|
|
|
RegisterConfiguration::kMaxFPRegisters)];
|
2014-11-27 09:19:31 +00:00
|
|
|
|
Revert of MIPS: Fix bad RegisterConfiguration usage in InstructionSequence unit tests. (patchset #3 id:40001 of https://codereview.chromium.org/2433093002/ )
Reason for revert:
This change rendered InstructionSequenceTest::SetNumRegs ineffectual, thus
loosening the tests that were using that API to ensure correct register
allocation under intentionally constrained setups.
For the problem stated in this CL, a solution needs to continue supporting the
intentionally set-up test configuration.
Original issue's description:
> MIPS: Fix bad RegisterConfiguration usage in InstructionSequence unit tests.
>
> Test InstructionSequenceTest has been initialized with a testing RegisterConfiguration
> instance defined in instruction-sequence-unittest.h, whereas class ExplicitOperand which
> is being tested used RegisterConfiguration from instruction.cc. In case these two
> instances are different, the tests would fail. The issue is fixed by using the same
> instance of RegisterConfiguration both for test code and code under test.
>
> Additionally, the tests in register-allocator-unittest.cc use hardcoded values
> for register and begin failing is the hardcoded register is not available for
> allocation. Fix by forcing the use of allocatable registers only.
>
> TEST=unittests.MoveOptimizerTest.RemovesRedundantExplicit,unittests.RegisterAllocatorTest.SpillPhi
> BUG=
>
> Committed: https://crrev.com/0cf56232209d4c9c669b8426680de18806f6c29a
> Cr-Commit-Position: refs/heads/master@{#40862}
TBR=dcarney@chromium.org,bmeurer@chromium.org,mstarzinger@chromium.org,vogelheim@chromium.org,titzer@chromium.org,ivica.bogosavljevic@imgtec.com
# Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=
Review-Url: https://codereview.chromium.org/2587593002
Cr-Commit-Position: refs/heads/master@{#41777}
2016-12-16 23:09:34 +00:00
|
|
|
namespace {
|
|
|
|
static int allocatable_codes[InstructionSequenceTest::kDefaultNRegs] = {
|
|
|
|
0, 1, 2, 3, 4, 5, 6, 7};
|
|
|
|
}
|
|
|
|
|
2014-11-27 09:19:31 +00:00
|
|
|
static void InitializeRegisterNames() {
|
|
|
|
char* loc = register_names_;
|
|
|
|
for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) {
|
|
|
|
general_register_names_[i] = loc;
|
|
|
|
loc += base::OS::SNPrintF(loc, 100, "gp_%d", i);
|
|
|
|
*loc++ = 0;
|
|
|
|
}
|
2016-04-25 20:10:32 +00:00
|
|
|
for (int i = 0; i < RegisterConfiguration::kMaxFPRegisters; ++i) {
|
2014-11-27 09:19:31 +00:00
|
|
|
double_register_names_[i] = loc;
|
|
|
|
loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1;
|
|
|
|
*loc++ = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
InstructionSequenceTest::InstructionSequenceTest()
|
|
|
|
: sequence_(nullptr),
|
|
|
|
num_general_registers_(kDefaultNRegs),
|
|
|
|
num_double_registers_(kDefaultNRegs),
|
|
|
|
instruction_blocks_(zone()),
|
|
|
|
current_block_(nullptr),
|
|
|
|
block_returns_(false) {
|
|
|
|
InitializeRegisterNames();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionSequenceTest::SetNumRegs(int num_general_registers,
|
|
|
|
int num_double_registers) {
|
2016-07-25 11:12:42 +00:00
|
|
|
CHECK(!config_);
|
2014-11-27 09:19:31 +00:00
|
|
|
CHECK(instructions_.empty());
|
|
|
|
CHECK(instruction_blocks_.empty());
|
|
|
|
num_general_registers_ = num_general_registers;
|
|
|
|
num_double_registers_ = num_double_registers;
|
|
|
|
}
|
|
|
|
|
2016-10-10 11:06:42 +00:00
|
|
|
int InstructionSequenceTest::GetNumRegs(MachineRepresentation rep) {
|
|
|
|
switch (rep) {
|
|
|
|
case MachineRepresentation::kFloat32:
|
|
|
|
return config()->num_float_registers();
|
|
|
|
case MachineRepresentation::kFloat64:
|
|
|
|
return config()->num_double_registers();
|
|
|
|
case MachineRepresentation::kSimd128:
|
|
|
|
return config()->num_simd128_registers();
|
|
|
|
default:
|
|
|
|
return config()->num_general_registers();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int InstructionSequenceTest::GetAllocatableCode(int index,
|
|
|
|
MachineRepresentation rep) {
|
|
|
|
switch (rep) {
|
|
|
|
case MachineRepresentation::kFloat32:
|
|
|
|
return config()->GetAllocatableFloatCode(index);
|
|
|
|
case MachineRepresentation::kFloat64:
|
|
|
|
return config()->GetAllocatableDoubleCode(index);
|
|
|
|
case MachineRepresentation::kSimd128:
|
|
|
|
return config()->GetAllocatableSimd128Code(index);
|
|
|
|
default:
|
|
|
|
return config()->GetAllocatableGeneralCode(index);
|
|
|
|
}
|
|
|
|
}
|
2014-11-27 09:19:31 +00:00
|
|
|
|
2016-12-23 10:51:08 +00:00
|
|
|
const RegisterConfiguration* InstructionSequenceTest::config() {
|
Revert of MIPS: Fix bad RegisterConfiguration usage in InstructionSequence unit tests. (patchset #3 id:40001 of https://codereview.chromium.org/2433093002/ )
Reason for revert:
This change rendered InstructionSequenceTest::SetNumRegs ineffectual, thus
loosening the tests that were using that API to ensure correct register
allocation under intentionally constrained setups.
For the problem stated in this CL, a solution needs to continue supporting the
intentionally set-up test configuration.
Original issue's description:
> MIPS: Fix bad RegisterConfiguration usage in InstructionSequence unit tests.
>
> Test InstructionSequenceTest has been initialized with a testing RegisterConfiguration
> instance defined in instruction-sequence-unittest.h, whereas class ExplicitOperand which
> is being tested used RegisterConfiguration from instruction.cc. In case these two
> instances are different, the tests would fail. The issue is fixed by using the same
> instance of RegisterConfiguration both for test code and code under test.
>
> Additionally, the tests in register-allocator-unittest.cc use hardcoded values
> for register and begin failing is the hardcoded register is not available for
> allocation. Fix by forcing the use of allocatable registers only.
>
> TEST=unittests.MoveOptimizerTest.RemovesRedundantExplicit,unittests.RegisterAllocatorTest.SpillPhi
> BUG=
>
> Committed: https://crrev.com/0cf56232209d4c9c669b8426680de18806f6c29a
> Cr-Commit-Position: refs/heads/master@{#40862}
TBR=dcarney@chromium.org,bmeurer@chromium.org,mstarzinger@chromium.org,vogelheim@chromium.org,titzer@chromium.org,ivica.bogosavljevic@imgtec.com
# Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=
Review-Url: https://codereview.chromium.org/2587593002
Cr-Commit-Position: refs/heads/master@{#41777}
2016-12-16 23:09:34 +00:00
|
|
|
if (!config_) {
|
|
|
|
config_.reset(new RegisterConfiguration(
|
|
|
|
num_general_registers_, num_double_registers_, num_general_registers_,
|
|
|
|
num_double_registers_, allocatable_codes, allocatable_codes,
|
|
|
|
kSimpleFPAliasing ? RegisterConfiguration::OVERLAP
|
|
|
|
: RegisterConfiguration::COMBINE,
|
|
|
|
general_register_names_,
|
|
|
|
double_register_names_, // float register names
|
|
|
|
double_register_names_,
|
|
|
|
double_register_names_)); // SIMD 128 register names
|
|
|
|
}
|
|
|
|
return config_.get();
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequence* InstructionSequenceTest::sequence() {
|
|
|
|
if (sequence_ == nullptr) {
|
2015-01-23 15:19:34 +00:00
|
|
|
sequence_ = new (zone())
|
|
|
|
InstructionSequence(isolate(), zone(), &instruction_blocks_);
|
2016-12-23 10:51:08 +00:00
|
|
|
sequence_->SetRegisterConfigurationForTesting(
|
|
|
|
InstructionSequenceTest::config());
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
return sequence_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionSequenceTest::StartLoop(int loop_blocks) {
|
|
|
|
CHECK(current_block_ == nullptr);
|
|
|
|
if (!loop_blocks_.empty()) {
|
|
|
|
CHECK(!loop_blocks_.back().loop_header_.IsValid());
|
|
|
|
}
|
|
|
|
LoopData loop_data = {Rpo::Invalid(), loop_blocks};
|
|
|
|
loop_blocks_.push_back(loop_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionSequenceTest::EndLoop() {
|
|
|
|
CHECK(current_block_ == nullptr);
|
|
|
|
CHECK(!loop_blocks_.empty());
|
|
|
|
CHECK_EQ(0, loop_blocks_.back().expected_blocks_);
|
|
|
|
loop_blocks_.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-06 16:21:23 +00:00
|
|
|
void InstructionSequenceTest::StartBlock(bool deferred) {
|
2014-11-27 09:19:31 +00:00
|
|
|
block_returns_ = false;
|
2015-08-06 16:21:23 +00:00
|
|
|
NewBlock(deferred);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EndBlock(BlockCompletion completion) {
|
|
|
|
Instruction* result = nullptr;
|
2014-11-27 09:19:31 +00:00
|
|
|
if (block_returns_) {
|
|
|
|
CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough);
|
|
|
|
completion.type_ = kBlockEnd;
|
|
|
|
}
|
|
|
|
switch (completion.type_) {
|
|
|
|
case kBlockEnd:
|
|
|
|
break;
|
|
|
|
case kFallThrough:
|
2015-04-30 13:39:11 +00:00
|
|
|
result = EmitJump();
|
2014-11-27 09:19:31 +00:00
|
|
|
break;
|
|
|
|
case kJump:
|
|
|
|
CHECK(!block_returns_);
|
2015-02-24 11:09:03 +00:00
|
|
|
result = EmitJump();
|
2014-11-27 09:19:31 +00:00
|
|
|
break;
|
|
|
|
case kBranch:
|
|
|
|
CHECK(!block_returns_);
|
2015-02-24 11:09:03 +00:00
|
|
|
result = EmitBranch(completion.op_);
|
2014-11-27 09:19:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
completions_.push_back(completion);
|
|
|
|
CHECK(current_block_ != nullptr);
|
|
|
|
sequence()->EndBlock(current_block_->rpo_number());
|
|
|
|
current_block_ = nullptr;
|
2015-02-24 11:09:03 +00:00
|
|
|
return result;
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequenceTest::TestOperand InstructionSequenceTest::Imm(int32_t imm) {
|
2015-04-09 14:06:19 +00:00
|
|
|
return TestOperand(kImmediate, imm);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequenceTest::VReg InstructionSequenceTest::Define(
|
|
|
|
TestOperand output_op) {
|
2016-10-10 11:06:42 +00:00
|
|
|
VReg vreg = NewReg(output_op);
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand outputs[1]{ConvertOutputOp(vreg, output_op)};
|
2015-02-24 11:09:03 +00:00
|
|
|
Emit(kArchNop, 1, outputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
return vreg;
|
|
|
|
}
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::Return(TestOperand input_op_0) {
|
2014-11-27 09:19:31 +00:00
|
|
|
block_returns_ = true;
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand inputs[1]{ConvertInputOp(input_op_0)};
|
2015-02-24 11:09:03 +00:00
|
|
|
return Emit(kArchRet, 0, nullptr, 1, inputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
|
|
|
|
VReg incoming_vreg_1,
|
|
|
|
VReg incoming_vreg_2,
|
|
|
|
VReg incoming_vreg_3) {
|
|
|
|
VReg inputs[] = {incoming_vreg_0, incoming_vreg_1, incoming_vreg_2,
|
|
|
|
incoming_vreg_3};
|
2015-02-09 13:02:41 +00:00
|
|
|
size_t input_count = 0;
|
|
|
|
for (; input_count < arraysize(inputs); ++input_count) {
|
|
|
|
if (inputs[input_count].value_ == kNoValue) break;
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
2015-02-09 13:02:41 +00:00
|
|
|
CHECK(input_count > 0);
|
|
|
|
auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, input_count);
|
|
|
|
for (size_t i = 0; i < input_count; ++i) {
|
|
|
|
SetInput(phi, i, inputs[i]);
|
|
|
|
}
|
|
|
|
current_block_->AddPhi(phi);
|
|
|
|
return phi;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
|
|
|
|
size_t input_count) {
|
|
|
|
auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, input_count);
|
|
|
|
SetInput(phi, 0, incoming_vreg_0);
|
2014-11-27 09:19:31 +00:00
|
|
|
current_block_->AddPhi(phi);
|
|
|
|
return phi;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-09 13:02:41 +00:00
|
|
|
void InstructionSequenceTest::SetInput(PhiInstruction* phi, size_t input,
|
|
|
|
VReg vreg) {
|
|
|
|
CHECK(vreg.value_ != kNoValue);
|
|
|
|
phi->SetInput(input, vreg.value_);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequenceTest::VReg InstructionSequenceTest::DefineConstant(
|
|
|
|
int32_t imm) {
|
|
|
|
VReg vreg = NewReg();
|
|
|
|
sequence()->AddConstant(vreg.value_, Constant(imm));
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand outputs[1]{ConstantOperand(vreg.value_)};
|
2015-02-24 11:09:03 +00:00
|
|
|
Emit(kArchNop, 1, outputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
return vreg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EmitNop() { return Emit(kArchNop); }
|
2014-11-27 09:19:31 +00:00
|
|
|
|
|
|
|
|
2014-12-12 11:15:13 +00:00
|
|
|
static size_t CountInputs(size_t size,
|
|
|
|
InstructionSequenceTest::TestOperand* inputs) {
|
|
|
|
size_t i = 0;
|
|
|
|
for (; i < size; ++i) {
|
|
|
|
if (inputs[i].type_ == InstructionSequenceTest::kInvalid) break;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EmitI(size_t input_size,
|
|
|
|
TestOperand* inputs) {
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
|
2015-02-24 11:09:03 +00:00
|
|
|
return Emit(kArchNop, 0, nullptr, input_size, mapped_inputs);
|
2014-12-12 11:15:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EmitI(TestOperand input_op_0,
|
|
|
|
TestOperand input_op_1,
|
|
|
|
TestOperand input_op_2,
|
|
|
|
TestOperand input_op_3) {
|
2014-12-12 11:15:13 +00:00
|
|
|
TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
|
|
|
|
return EmitI(CountInputs(arraysize(inputs), inputs), inputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
|
2014-12-12 11:15:13 +00:00
|
|
|
TestOperand output_op, size_t input_size, TestOperand* inputs) {
|
2016-10-10 11:06:42 +00:00
|
|
|
VReg output_vreg = NewReg(output_op);
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand outputs[1]{ConvertOutputOp(output_vreg, output_op)};
|
|
|
|
InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
|
2015-02-24 11:09:03 +00:00
|
|
|
Emit(kArchNop, 1, outputs, input_size, mapped_inputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
return output_vreg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-12 11:15:13 +00:00
|
|
|
InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
|
|
|
|
TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
|
|
|
|
TestOperand input_op_2, TestOperand input_op_3) {
|
|
|
|
TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
|
|
|
|
return EmitOI(output_op, CountInputs(arraysize(inputs), inputs), inputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
InstructionSequenceTest::VRegPair InstructionSequenceTest::EmitOOI(
|
|
|
|
TestOperand output_op_0, TestOperand output_op_1, size_t input_size,
|
|
|
|
TestOperand* inputs) {
|
2016-10-10 11:06:42 +00:00
|
|
|
VRegPair output_vregs =
|
|
|
|
std::make_pair(NewReg(output_op_0), NewReg(output_op_1));
|
2015-02-24 11:09:03 +00:00
|
|
|
InstructionOperand outputs[2]{
|
|
|
|
ConvertOutputOp(output_vregs.first, output_op_0),
|
|
|
|
ConvertOutputOp(output_vregs.second, output_op_1)};
|
|
|
|
InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
|
|
|
|
Emit(kArchNop, 2, outputs, input_size, mapped_inputs);
|
|
|
|
return output_vregs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequenceTest::VRegPair InstructionSequenceTest::EmitOOI(
|
|
|
|
TestOperand output_op_0, TestOperand output_op_1, TestOperand input_op_0,
|
|
|
|
TestOperand input_op_1, TestOperand input_op_2, TestOperand input_op_3) {
|
|
|
|
TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
|
|
|
|
return EmitOOI(output_op_0, output_op_1,
|
|
|
|
CountInputs(arraysize(inputs), inputs), inputs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-27 09:19:31 +00:00
|
|
|
InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
|
|
|
|
TestOperand output_op, size_t input_size, TestOperand* inputs) {
|
2016-10-10 11:06:42 +00:00
|
|
|
VReg output_vreg = NewReg(output_op);
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand outputs[1]{ConvertOutputOp(output_vreg, output_op)};
|
|
|
|
CHECK(UnallocatedOperand::cast(outputs[0]).HasFixedPolicy());
|
|
|
|
InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
|
2015-02-24 11:09:03 +00:00
|
|
|
Emit(kArchCallCodeObject, 1, outputs, input_size, mapped_inputs, 0, nullptr,
|
|
|
|
true);
|
2014-11-27 09:19:31 +00:00
|
|
|
return output_vreg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
|
|
|
|
TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
|
|
|
|
TestOperand input_op_2, TestOperand input_op_3) {
|
|
|
|
TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
|
2014-12-12 11:15:13 +00:00
|
|
|
return EmitCall(output_op, CountInputs(arraysize(inputs), inputs), inputs);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EmitBranch(TestOperand input_op) {
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand inputs[4]{ConvertInputOp(input_op), ConvertInputOp(Imm()),
|
|
|
|
ConvertInputOp(Imm()), ConvertInputOp(Imm())};
|
2014-11-27 09:19:31 +00:00
|
|
|
InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
|
|
|
|
FlagsConditionField::encode(kEqual);
|
2015-03-24 14:05:21 +00:00
|
|
|
auto instruction = NewInstruction(opcode, 0, nullptr, 4, inputs);
|
2015-02-24 11:09:03 +00:00
|
|
|
return AddInstruction(instruction);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EmitFallThrough() {
|
2015-03-24 14:05:21 +00:00
|
|
|
auto instruction = NewInstruction(kArchNop, 0, nullptr);
|
2015-02-24 11:09:03 +00:00
|
|
|
return AddInstruction(instruction);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::EmitJump() {
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand inputs[1]{ConvertInputOp(Imm())};
|
2015-03-24 14:05:21 +00:00
|
|
|
auto instruction = NewInstruction(kArchJmp, 0, nullptr, 1, inputs);
|
2015-02-24 11:09:03 +00:00
|
|
|
return AddInstruction(instruction);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Instruction* InstructionSequenceTest::NewInstruction(
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionCode code, size_t outputs_size, InstructionOperand* outputs,
|
|
|
|
size_t inputs_size, InstructionOperand* inputs, size_t temps_size,
|
|
|
|
InstructionOperand* temps) {
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(current_block_);
|
2014-11-27 09:19:31 +00:00
|
|
|
return Instruction::New(zone(), code, outputs_size, outputs, inputs_size,
|
|
|
|
inputs, temps_size, temps);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand InstructionSequenceTest::Unallocated(
|
2014-11-27 09:19:31 +00:00
|
|
|
TestOperand op, UnallocatedOperand::ExtendedPolicy policy) {
|
2015-02-04 12:38:47 +00:00
|
|
|
return UnallocatedOperand(policy, op.vreg_.value_);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand InstructionSequenceTest::Unallocated(
|
2014-11-27 09:19:31 +00:00
|
|
|
TestOperand op, UnallocatedOperand::ExtendedPolicy policy,
|
|
|
|
UnallocatedOperand::Lifetime lifetime) {
|
2015-02-04 12:38:47 +00:00
|
|
|
return UnallocatedOperand(policy, lifetime, op.vreg_.value_);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand InstructionSequenceTest::Unallocated(
|
2014-11-27 09:19:31 +00:00
|
|
|
TestOperand op, UnallocatedOperand::ExtendedPolicy policy, int index) {
|
2015-02-04 12:38:47 +00:00
|
|
|
return UnallocatedOperand(policy, index, op.vreg_.value_);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand InstructionSequenceTest::Unallocated(
|
2014-11-27 09:19:31 +00:00
|
|
|
TestOperand op, UnallocatedOperand::BasicPolicy policy, int index) {
|
2015-02-04 12:38:47 +00:00
|
|
|
return UnallocatedOperand(policy, index, op.vreg_.value_);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand* InstructionSequenceTest::ConvertInputs(
|
2014-12-12 11:15:13 +00:00
|
|
|
size_t input_size, TestOperand* inputs) {
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand* mapped_inputs =
|
|
|
|
zone()->NewArray<InstructionOperand>(static_cast<int>(input_size));
|
2014-12-12 11:15:13 +00:00
|
|
|
for (size_t i = 0; i < input_size; ++i) {
|
|
|
|
mapped_inputs[i] = ConvertInputOp(inputs[i]);
|
|
|
|
}
|
|
|
|
return mapped_inputs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand InstructionSequenceTest::ConvertInputOp(TestOperand op) {
|
2014-11-27 09:19:31 +00:00
|
|
|
if (op.type_ == kImmediate) {
|
|
|
|
CHECK_EQ(op.vreg_.value_, kNoValue);
|
2015-04-09 14:06:19 +00:00
|
|
|
return ImmediateOperand(ImmediateOperand::INLINE, op.value_);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(op.vreg_.value_, kNoValue);
|
|
|
|
switch (op.type_) {
|
|
|
|
case kNone:
|
|
|
|
return Unallocated(op, UnallocatedOperand::NONE,
|
|
|
|
UnallocatedOperand::USED_AT_START);
|
2014-12-12 11:15:13 +00:00
|
|
|
case kUnique:
|
|
|
|
return Unallocated(op, UnallocatedOperand::NONE);
|
|
|
|
case kUniqueRegister:
|
|
|
|
return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
|
2014-11-27 09:19:31 +00:00
|
|
|
case kRegister:
|
|
|
|
return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER,
|
|
|
|
UnallocatedOperand::USED_AT_START);
|
2015-03-23 16:03:14 +00:00
|
|
|
case kSlot:
|
|
|
|
return Unallocated(op, UnallocatedOperand::MUST_HAVE_SLOT,
|
|
|
|
UnallocatedOperand::USED_AT_START);
|
2016-10-10 11:06:42 +00:00
|
|
|
case kFixedRegister: {
|
|
|
|
MachineRepresentation rep = GetCanonicalRep(op);
|
|
|
|
CHECK(0 <= op.value_ && op.value_ < GetNumRegs(rep));
|
|
|
|
if (DoesRegisterAllocation()) {
|
|
|
|
auto extended_policy = IsFloatingPoint(rep)
|
|
|
|
? UnallocatedOperand::FIXED_FP_REGISTER
|
|
|
|
: UnallocatedOperand::FIXED_REGISTER;
|
|
|
|
return Unallocated(op, extended_policy, op.value_);
|
|
|
|
} else {
|
|
|
|
return AllocatedOperand(LocationOperand::REGISTER, rep, op.value_);
|
|
|
|
}
|
|
|
|
}
|
2014-11-27 09:19:31 +00:00
|
|
|
case kFixedSlot:
|
2016-10-10 11:06:42 +00:00
|
|
|
if (DoesRegisterAllocation()) {
|
|
|
|
return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
|
|
|
|
} else {
|
|
|
|
return AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
|
|
GetCanonicalRep(op), op.value_);
|
|
|
|
}
|
2014-11-27 09:19:31 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CHECK(false);
|
2015-02-04 12:38:47 +00:00
|
|
|
return InstructionOperand();
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-04 12:38:47 +00:00
|
|
|
InstructionOperand InstructionSequenceTest::ConvertOutputOp(VReg vreg,
|
|
|
|
TestOperand op) {
|
2014-11-27 09:19:31 +00:00
|
|
|
CHECK_EQ(op.vreg_.value_, kNoValue);
|
|
|
|
op.vreg_ = vreg;
|
|
|
|
switch (op.type_) {
|
|
|
|
case kSameAsFirst:
|
|
|
|
return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT);
|
|
|
|
case kRegister:
|
|
|
|
return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
|
|
|
|
case kFixedSlot:
|
2016-10-10 11:06:42 +00:00
|
|
|
if (DoesRegisterAllocation()) {
|
|
|
|
return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
|
|
|
|
} else {
|
|
|
|
return AllocatedOperand(LocationOperand::STACK_SLOT,
|
|
|
|
GetCanonicalRep(op), op.value_);
|
|
|
|
}
|
|
|
|
case kFixedRegister: {
|
|
|
|
MachineRepresentation rep = GetCanonicalRep(op);
|
|
|
|
CHECK(0 <= op.value_ && op.value_ < GetNumRegs(rep));
|
|
|
|
if (DoesRegisterAllocation()) {
|
|
|
|
auto extended_policy = IsFloatingPoint(rep)
|
|
|
|
? UnallocatedOperand::FIXED_FP_REGISTER
|
|
|
|
: UnallocatedOperand::FIXED_REGISTER;
|
|
|
|
return Unallocated(op, extended_policy, op.value_);
|
|
|
|
} else {
|
|
|
|
return AllocatedOperand(LocationOperand::REGISTER, rep, op.value_);
|
|
|
|
}
|
|
|
|
}
|
2014-11-27 09:19:31 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CHECK(false);
|
2015-02-04 12:38:47 +00:00
|
|
|
return InstructionOperand();
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-06 16:21:23 +00:00
|
|
|
InstructionBlock* InstructionSequenceTest::NewBlock(bool deferred) {
|
2014-11-27 09:19:31 +00:00
|
|
|
CHECK(current_block_ == nullptr);
|
2015-02-25 16:37:49 +00:00
|
|
|
Rpo rpo = Rpo::FromInt(static_cast<int>(instruction_blocks_.size()));
|
2014-11-27 09:19:31 +00:00
|
|
|
Rpo loop_header = Rpo::Invalid();
|
|
|
|
Rpo loop_end = Rpo::Invalid();
|
|
|
|
if (!loop_blocks_.empty()) {
|
|
|
|
auto& loop_data = loop_blocks_.back();
|
|
|
|
// This is a loop header.
|
|
|
|
if (!loop_data.loop_header_.IsValid()) {
|
2015-02-25 16:37:49 +00:00
|
|
|
loop_end = Rpo::FromInt(rpo.ToInt() + loop_data.expected_blocks_);
|
2014-11-27 09:19:31 +00:00
|
|
|
loop_data.expected_blocks_--;
|
|
|
|
loop_data.loop_header_ = rpo;
|
|
|
|
} else {
|
|
|
|
// This is a loop body.
|
|
|
|
CHECK_NE(0, loop_data.expected_blocks_);
|
|
|
|
// TODO(dcarney): handle nested loops.
|
|
|
|
loop_data.expected_blocks_--;
|
|
|
|
loop_header = loop_data.loop_header_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Construct instruction block.
|
2015-06-17 05:40:28 +00:00
|
|
|
auto instruction_block = new (zone())
|
2015-08-06 16:21:23 +00:00
|
|
|
InstructionBlock(zone(), rpo, loop_header, loop_end, deferred, false);
|
2014-11-27 09:19:31 +00:00
|
|
|
instruction_blocks_.push_back(instruction_block);
|
|
|
|
current_block_ = instruction_block;
|
|
|
|
sequence()->StartBlock(rpo);
|
|
|
|
return instruction_block;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionSequenceTest::WireBlocks() {
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!current_block());
|
2014-11-27 09:19:31 +00:00
|
|
|
CHECK(instruction_blocks_.size() == completions_.size());
|
2015-04-30 13:39:11 +00:00
|
|
|
CHECK(loop_blocks_.empty());
|
|
|
|
// Wire in end block to look like a scheduler produced cfg.
|
|
|
|
auto end_block = NewBlock();
|
|
|
|
current_block_ = nullptr;
|
|
|
|
sequence()->EndBlock(end_block->rpo_number());
|
2014-11-27 09:19:31 +00:00
|
|
|
size_t offset = 0;
|
|
|
|
for (const auto& completion : completions_) {
|
|
|
|
switch (completion.type_) {
|
2015-04-30 13:39:11 +00:00
|
|
|
case kBlockEnd: {
|
|
|
|
auto block = instruction_blocks_[offset];
|
|
|
|
block->successors().push_back(end_block->rpo_number());
|
|
|
|
end_block->predecessors().push_back(block->rpo_number());
|
2014-11-27 09:19:31 +00:00
|
|
|
break;
|
2015-04-30 13:39:11 +00:00
|
|
|
}
|
2014-11-27 09:19:31 +00:00
|
|
|
case kFallThrough: // Fallthrough.
|
|
|
|
case kJump:
|
|
|
|
WireBlock(offset, completion.offset_0_);
|
|
|
|
break;
|
|
|
|
case kBranch:
|
|
|
|
WireBlock(offset, completion.offset_0_);
|
|
|
|
WireBlock(offset, completion.offset_1_);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionSequenceTest::WireBlock(size_t block_offset, int jump_offset) {
|
|
|
|
size_t target_block_offset = block_offset + static_cast<size_t>(jump_offset);
|
|
|
|
CHECK(block_offset < instruction_blocks_.size());
|
|
|
|
CHECK(target_block_offset < instruction_blocks_.size());
|
|
|
|
auto block = instruction_blocks_[block_offset];
|
|
|
|
auto target = instruction_blocks_[target_block_offset];
|
|
|
|
block->successors().push_back(target->rpo_number());
|
|
|
|
target->predecessors().push_back(block->rpo_number());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::Emit(
|
|
|
|
InstructionCode code, size_t outputs_size, InstructionOperand* outputs,
|
|
|
|
size_t inputs_size, InstructionOperand* inputs, size_t temps_size,
|
|
|
|
InstructionOperand* temps, bool is_call) {
|
2014-11-27 09:19:31 +00:00
|
|
|
auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size,
|
|
|
|
inputs, temps_size, temps);
|
|
|
|
if (is_call) instruction->MarkAsCall();
|
2015-02-24 11:09:03 +00:00
|
|
|
return AddInstruction(instruction);
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-24 11:09:03 +00:00
|
|
|
Instruction* InstructionSequenceTest::AddInstruction(Instruction* instruction) {
|
2014-11-27 09:19:31 +00:00
|
|
|
sequence()->AddInstruction(instruction);
|
2015-02-24 11:09:03 +00:00
|
|
|
return instruction;
|
2014-11-27 09:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|