[turbofan] extend register allocator testing with control flow
R=bmeurer@chromium.org, jarin@chromium.org BUG= Review URL: https://codereview.chromium.org/699083003 Cr-Commit-Position: refs/heads/master@{#25178} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25178 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
a7277a9b8c
commit
57b42dc51f
@ -66,11 +66,11 @@ void InstructionSelector::SelectInstructions() {
|
||||
sequence()->InstructionBlockAt(block->GetRpoNumber());
|
||||
size_t end = instruction_block->code_end();
|
||||
size_t start = instruction_block->code_start();
|
||||
sequence()->StartBlock(block);
|
||||
sequence()->StartBlock(block->GetRpoNumber());
|
||||
while (start-- > end) {
|
||||
sequence()->AddInstruction(instructions_[start]);
|
||||
}
|
||||
sequence()->EndBlock(block);
|
||||
sequence()->EndBlock(block->GetRpoNumber());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,45 +338,23 @@ std::ostream& operator<<(std::ostream& os, const Constant& constant) {
|
||||
}
|
||||
|
||||
|
||||
static BasicBlock::RpoNumber GetRpo(BasicBlock* block) {
|
||||
if (block == NULL) return BasicBlock::RpoNumber::Invalid();
|
||||
return block->GetRpoNumber();
|
||||
}
|
||||
|
||||
|
||||
static BasicBlock::RpoNumber GetLoopEndRpo(const BasicBlock* block) {
|
||||
if (!block->IsLoopHeader()) return BasicBlock::RpoNumber::Invalid();
|
||||
return block->loop_end()->GetRpoNumber();
|
||||
}
|
||||
|
||||
|
||||
InstructionBlock::InstructionBlock(Zone* zone, const BasicBlock* block)
|
||||
: successors_(static_cast<int>(block->SuccessorCount()),
|
||||
BasicBlock::RpoNumber::Invalid(), zone),
|
||||
predecessors_(static_cast<int>(block->PredecessorCount()),
|
||||
BasicBlock::RpoNumber::Invalid(), zone),
|
||||
InstructionBlock::InstructionBlock(Zone* zone, BasicBlock::Id id,
|
||||
BasicBlock::RpoNumber ao_number,
|
||||
BasicBlock::RpoNumber rpo_number,
|
||||
BasicBlock::RpoNumber loop_header,
|
||||
BasicBlock::RpoNumber loop_end,
|
||||
bool deferred)
|
||||
: successors_(zone),
|
||||
predecessors_(zone),
|
||||
phis_(zone),
|
||||
id_(block->id()),
|
||||
ao_number_(block->GetAoNumber()),
|
||||
rpo_number_(block->GetRpoNumber()),
|
||||
loop_header_(GetRpo(block->loop_header())),
|
||||
loop_end_(GetLoopEndRpo(block)),
|
||||
id_(id),
|
||||
ao_number_(ao_number),
|
||||
rpo_number_(rpo_number),
|
||||
loop_header_(loop_header),
|
||||
loop_end_(loop_end),
|
||||
code_start_(-1),
|
||||
code_end_(-1),
|
||||
deferred_(block->deferred()) {
|
||||
// Map successors and precessors
|
||||
size_t index = 0;
|
||||
for (BasicBlock::Successors::const_iterator it = block->successors_begin();
|
||||
it != block->successors_end(); ++it, ++index) {
|
||||
successors_[index] = (*it)->GetRpoNumber();
|
||||
}
|
||||
index = 0;
|
||||
for (BasicBlock::Predecessors::const_iterator
|
||||
it = block->predecessors_begin();
|
||||
it != block->predecessors_end(); ++it, ++index) {
|
||||
predecessors_[index] = (*it)->GetRpoNumber();
|
||||
}
|
||||
}
|
||||
deferred_(deferred) {}
|
||||
|
||||
|
||||
size_t InstructionBlock::PredecessorIndexOf(
|
||||
@ -390,6 +368,38 @@ size_t InstructionBlock::PredecessorIndexOf(
|
||||
}
|
||||
|
||||
|
||||
static BasicBlock::RpoNumber GetRpo(BasicBlock* block) {
|
||||
if (block == NULL) return BasicBlock::RpoNumber::Invalid();
|
||||
return block->GetRpoNumber();
|
||||
}
|
||||
|
||||
|
||||
static BasicBlock::RpoNumber GetLoopEndRpo(const BasicBlock* block) {
|
||||
if (!block->IsLoopHeader()) return BasicBlock::RpoNumber::Invalid();
|
||||
return block->loop_end()->GetRpoNumber();
|
||||
}
|
||||
|
||||
|
||||
static InstructionBlock* InstructionBlockFor(Zone* zone,
|
||||
const BasicBlock* block) {
|
||||
InstructionBlock* instr_block = new (zone) InstructionBlock(
|
||||
zone, block->id(), block->GetAoNumber(), block->GetRpoNumber(),
|
||||
GetRpo(block->loop_header()), GetLoopEndRpo(block), block->deferred());
|
||||
// Map successors and precessors
|
||||
instr_block->successors().reserve(block->SuccessorCount());
|
||||
for (auto it = block->successors_begin(); it != block->successors_end();
|
||||
++it) {
|
||||
instr_block->successors().push_back((*it)->GetRpoNumber());
|
||||
}
|
||||
instr_block->predecessors().reserve(block->PredecessorCount());
|
||||
for (auto it = block->predecessors_begin(); it != block->predecessors_end();
|
||||
++it) {
|
||||
instr_block->predecessors().push_back((*it)->GetRpoNumber());
|
||||
}
|
||||
return instr_block;
|
||||
}
|
||||
|
||||
|
||||
InstructionBlocks* InstructionSequence::InstructionBlocksFor(
|
||||
Zone* zone, const Schedule* schedule) {
|
||||
InstructionBlocks* blocks = zone->NewArray<InstructionBlocks>(1);
|
||||
@ -400,7 +410,7 @@ InstructionBlocks* InstructionSequence::InstructionBlocksFor(
|
||||
it != schedule->rpo_order()->end(); ++it, ++rpo_number) {
|
||||
DCHECK_EQ(NULL, (*blocks)[rpo_number]);
|
||||
DCHECK((*it)->GetRpoNumber().ToSize() == rpo_number);
|
||||
(*blocks)[rpo_number] = new (zone) InstructionBlock(zone, *it);
|
||||
(*blocks)[rpo_number] = InstructionBlockFor(zone, *it);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
@ -436,18 +446,18 @@ BlockStartInstruction* InstructionSequence::GetBlockStart(
|
||||
}
|
||||
|
||||
|
||||
void InstructionSequence::StartBlock(BasicBlock* basic_block) {
|
||||
InstructionBlock* block = InstructionBlockAt(basic_block->GetRpoNumber());
|
||||
void InstructionSequence::StartBlock(BasicBlock::RpoNumber rpo) {
|
||||
InstructionBlock* block = InstructionBlockAt(rpo);
|
||||
block->set_code_start(static_cast<int>(instructions_.size()));
|
||||
BlockStartInstruction* block_start =
|
||||
BlockStartInstruction::New(zone(), basic_block);
|
||||
BlockStartInstruction::New(zone(), block->id(), rpo);
|
||||
AddInstruction(block_start);
|
||||
}
|
||||
|
||||
|
||||
void InstructionSequence::EndBlock(BasicBlock* basic_block) {
|
||||
void InstructionSequence::EndBlock(BasicBlock::RpoNumber rpo) {
|
||||
int end = static_cast<int>(instructions_.size());
|
||||
InstructionBlock* block = InstructionBlockAt(basic_block->GetRpoNumber());
|
||||
InstructionBlock* block = InstructionBlockAt(rpo);
|
||||
DCHECK(block->code_start() >= 0 && block->code_start() < end);
|
||||
block->set_code_end(end);
|
||||
}
|
||||
|
@ -624,9 +624,10 @@ class BlockStartInstruction FINAL : public GapInstruction {
|
||||
BasicBlock::RpoNumber rpo_number() const { return rpo_number_; }
|
||||
BasicBlock::Id id() const { return id_; }
|
||||
|
||||
static BlockStartInstruction* New(Zone* zone, BasicBlock* block) {
|
||||
static BlockStartInstruction* New(Zone* zone, BasicBlock::Id id,
|
||||
BasicBlock::RpoNumber rpo_number) {
|
||||
void* buffer = zone->New(sizeof(BlockStartInstruction));
|
||||
return new (buffer) BlockStartInstruction(block);
|
||||
return new (buffer) BlockStartInstruction(id, rpo_number);
|
||||
}
|
||||
|
||||
static BlockStartInstruction* cast(Instruction* instr) {
|
||||
@ -635,10 +636,10 @@ class BlockStartInstruction FINAL : public GapInstruction {
|
||||
}
|
||||
|
||||
private:
|
||||
explicit BlockStartInstruction(BasicBlock* block)
|
||||
BlockStartInstruction(BasicBlock::Id id, BasicBlock::RpoNumber rpo_number)
|
||||
: GapInstruction(kBlockStartInstruction),
|
||||
id_(block->id()),
|
||||
rpo_number_(block->GetRpoNumber()) {}
|
||||
id_(id),
|
||||
rpo_number_(rpo_number) {}
|
||||
|
||||
BasicBlock::Id id_;
|
||||
BasicBlock::RpoNumber rpo_number_;
|
||||
@ -799,7 +800,11 @@ class PhiInstruction FINAL : public ZoneObject {
|
||||
// Analogue of BasicBlock for Instructions instead of Nodes.
|
||||
class InstructionBlock FINAL : public ZoneObject {
|
||||
public:
|
||||
explicit InstructionBlock(Zone* zone, const BasicBlock* block);
|
||||
InstructionBlock(Zone* zone, BasicBlock::Id id,
|
||||
BasicBlock::RpoNumber ao_number,
|
||||
BasicBlock::RpoNumber rpo_number,
|
||||
BasicBlock::RpoNumber loop_header,
|
||||
BasicBlock::RpoNumber loop_end, bool deferred);
|
||||
|
||||
// Instruction indexes (used by the register allocator).
|
||||
int first_instruction_index() const {
|
||||
@ -943,8 +948,8 @@ class InstructionSequence FINAL {
|
||||
|
||||
// Used by the instruction selector while adding instructions.
|
||||
int AddInstruction(Instruction* instr);
|
||||
void StartBlock(BasicBlock* block);
|
||||
void EndBlock(BasicBlock* block);
|
||||
void StartBlock(BasicBlock::RpoNumber rpo);
|
||||
void EndBlock(BasicBlock::RpoNumber rpo);
|
||||
|
||||
int AddConstant(int virtual_register, Constant constant) {
|
||||
DCHECK(virtual_register >= 0 && virtual_register < next_virtual_register_);
|
||||
|
@ -158,23 +158,23 @@ TEST(InstructionGetBasicBlock) {
|
||||
|
||||
R.allocCode();
|
||||
|
||||
R.code->StartBlock(b0);
|
||||
R.code->StartBlock(b0->GetRpoNumber());
|
||||
int i0 = R.NewInstr();
|
||||
int i1 = R.NewInstr();
|
||||
R.code->EndBlock(b0);
|
||||
R.code->StartBlock(b1);
|
||||
R.code->EndBlock(b0->GetRpoNumber());
|
||||
R.code->StartBlock(b1->GetRpoNumber());
|
||||
int i2 = R.NewInstr();
|
||||
int i3 = R.NewInstr();
|
||||
int i4 = R.NewInstr();
|
||||
int i5 = R.NewInstr();
|
||||
R.code->EndBlock(b1);
|
||||
R.code->StartBlock(b2);
|
||||
R.code->EndBlock(b1->GetRpoNumber());
|
||||
R.code->StartBlock(b2->GetRpoNumber());
|
||||
int i6 = R.NewInstr();
|
||||
int i7 = R.NewInstr();
|
||||
int i8 = R.NewInstr();
|
||||
R.code->EndBlock(b2);
|
||||
R.code->StartBlock(b3);
|
||||
R.code->EndBlock(b3);
|
||||
R.code->EndBlock(b2->GetRpoNumber());
|
||||
R.code->StartBlock(b3->GetRpoNumber());
|
||||
R.code->EndBlock(b3->GetRpoNumber());
|
||||
|
||||
CHECK_EQ(b0, R.GetBasicBlock(i0));
|
||||
CHECK_EQ(b0, R.GetBasicBlock(i1));
|
||||
@ -211,10 +211,10 @@ TEST(InstructionIsGapAt) {
|
||||
R.allocCode();
|
||||
TestInstr* i0 = TestInstr::New(R.zone(), 100);
|
||||
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
|
||||
R.code->StartBlock(b0);
|
||||
R.code->StartBlock(b0->GetRpoNumber());
|
||||
R.code->AddInstruction(i0);
|
||||
R.code->AddInstruction(g);
|
||||
R.code->EndBlock(b0);
|
||||
R.code->EndBlock(b0->GetRpoNumber());
|
||||
|
||||
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
|
||||
|
||||
@ -238,17 +238,17 @@ TEST(InstructionIsGapAt2) {
|
||||
R.allocCode();
|
||||
TestInstr* i0 = TestInstr::New(R.zone(), 100);
|
||||
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
|
||||
R.code->StartBlock(b0);
|
||||
R.code->StartBlock(b0->GetRpoNumber());
|
||||
R.code->AddInstruction(i0);
|
||||
R.code->AddInstruction(g);
|
||||
R.code->EndBlock(b0);
|
||||
R.code->EndBlock(b0->GetRpoNumber());
|
||||
|
||||
TestInstr* i1 = TestInstr::New(R.zone(), 102);
|
||||
TestInstr* g1 = TestInstr::New(R.zone(), 104)->MarkAsControl();
|
||||
R.code->StartBlock(b1);
|
||||
R.code->StartBlock(b1->GetRpoNumber());
|
||||
R.code->AddInstruction(i1);
|
||||
R.code->AddInstruction(g1);
|
||||
R.code->EndBlock(b1);
|
||||
R.code->EndBlock(b1->GetRpoNumber());
|
||||
|
||||
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
|
||||
|
||||
@ -279,10 +279,10 @@ TEST(InstructionAddGapMove) {
|
||||
R.allocCode();
|
||||
TestInstr* i0 = TestInstr::New(R.zone(), 100);
|
||||
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
|
||||
R.code->StartBlock(b0);
|
||||
R.code->StartBlock(b0->GetRpoNumber());
|
||||
R.code->AddInstruction(i0);
|
||||
R.code->AddInstruction(g);
|
||||
R.code->EndBlock(b0);
|
||||
R.code->EndBlock(b0->GetRpoNumber());
|
||||
|
||||
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
|
||||
|
||||
|
@ -37,11 +37,37 @@ static void InitializeRegisterNames() {
|
||||
}
|
||||
}
|
||||
|
||||
enum BlockCompletionType { kFallThrough, kBranch, kJump };
|
||||
|
||||
struct BlockCompletion {
|
||||
BlockCompletionType type_;
|
||||
int vreg_;
|
||||
int offset_0_;
|
||||
int offset_1_;
|
||||
};
|
||||
|
||||
static const int kInvalidJumpOffset = kMinInt;
|
||||
|
||||
BlockCompletion FallThrough() {
|
||||
BlockCompletion completion = {kFallThrough, -1, 1, kInvalidJumpOffset};
|
||||
return completion;
|
||||
}
|
||||
|
||||
|
||||
BlockCompletion Jump(int offset) {
|
||||
BlockCompletion completion = {kJump, -1, offset, kInvalidJumpOffset};
|
||||
return completion;
|
||||
}
|
||||
|
||||
|
||||
BlockCompletion Branch(int vreg, int left_offset, int right_offset) {
|
||||
BlockCompletion completion = {kBranch, vreg, left_offset, right_offset};
|
||||
return completion;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// TODO(dcarney): fake opcodes.
|
||||
// TODO(dcarney): fix printing of sequence w.r.t fake opcodes and registers.
|
||||
class RegisterAllocatorTest : public TestWithZone {
|
||||
public:
|
||||
static const int kDefaultNRegs = 4;
|
||||
@ -49,12 +75,18 @@ class RegisterAllocatorTest : public TestWithZone {
|
||||
RegisterAllocatorTest()
|
||||
: num_general_registers_(kDefaultNRegs),
|
||||
num_double_registers_(kDefaultNRegs),
|
||||
basic_blocks_(zone()),
|
||||
instruction_blocks_(zone()),
|
||||
current_block_(NULL) {
|
||||
current_block_(nullptr),
|
||||
is_last_block_(false) {
|
||||
InitializeRegisterNames();
|
||||
}
|
||||
|
||||
void SetNumRegs(int num_general_registers, int num_double_registers) {
|
||||
CHECK(instruction_blocks_.empty());
|
||||
num_general_registers_ = num_general_registers;
|
||||
num_double_registers_ = num_double_registers;
|
||||
}
|
||||
|
||||
RegisterConfiguration* config() {
|
||||
if (config_.is_empty()) {
|
||||
config_.Reset(new RegisterConfiguration(
|
||||
@ -86,42 +118,64 @@ class RegisterAllocatorTest : public TestWithZone {
|
||||
return allocator_.get();
|
||||
}
|
||||
|
||||
InstructionBlock* StartBlock(Rpo loop_header = Rpo::Invalid(),
|
||||
Rpo loop_end = Rpo::Invalid()) {
|
||||
CHECK(current_block_ == NULL);
|
||||
BasicBlock::Id block_id =
|
||||
BasicBlock::Id::FromSize(instruction_blocks_.size());
|
||||
BasicBlock* basic_block = new (zone()) BasicBlock(zone(), block_id);
|
||||
basic_block->set_rpo_number(block_id.ToInt());
|
||||
basic_block->set_ao_number(block_id.ToInt());
|
||||
if (loop_header.IsValid()) {
|
||||
basic_block->set_loop_depth(1);
|
||||
basic_block->set_loop_header(basic_blocks_[loop_header.ToSize()]);
|
||||
basic_block->set_loop_end(basic_blocks_[loop_end.ToSize()]);
|
||||
void StartLoop(int loop_blocks) {
|
||||
CHECK(current_block_ == nullptr);
|
||||
if (!loop_blocks_.empty()) {
|
||||
CHECK(!loop_blocks_.back().loop_header_.IsValid());
|
||||
}
|
||||
InstructionBlock* instruction_block =
|
||||
new (zone()) InstructionBlock(zone(), basic_block);
|
||||
basic_blocks_.push_back(basic_block);
|
||||
instruction_blocks_.push_back(instruction_block);
|
||||
current_block_ = instruction_block;
|
||||
sequence()->StartBlock(basic_block);
|
||||
return instruction_block;
|
||||
LoopData loop_data = {Rpo::Invalid(), loop_blocks};
|
||||
loop_blocks_.push_back(loop_data);
|
||||
}
|
||||
|
||||
void EndBlock() {
|
||||
CHECK(current_block_ != NULL);
|
||||
sequence()->EndBlock(basic_blocks_[current_block_->rpo_number().ToSize()]);
|
||||
current_block_ = NULL;
|
||||
void EndLoop() {
|
||||
CHECK(current_block_ == nullptr);
|
||||
CHECK(!loop_blocks_.empty());
|
||||
CHECK_EQ(0, loop_blocks_.back().expected_blocks_);
|
||||
loop_blocks_.pop_back();
|
||||
}
|
||||
|
||||
void StartLastBlock() {
|
||||
CHECK(!is_last_block_);
|
||||
is_last_block_ = true;
|
||||
NewBlock();
|
||||
}
|
||||
|
||||
void StartBlock() {
|
||||
CHECK(!is_last_block_);
|
||||
NewBlock();
|
||||
}
|
||||
|
||||
void EndBlock(BlockCompletion completion = FallThrough()) {
|
||||
completions_.push_back(completion);
|
||||
switch (completion.type_) {
|
||||
case kFallThrough:
|
||||
if (is_last_block_) break;
|
||||
// TODO(dcarney): we don't emit this after returns.
|
||||
EmitFallThrough();
|
||||
break;
|
||||
case kJump:
|
||||
EmitJump();
|
||||
break;
|
||||
case kBranch:
|
||||
EmitBranch(completion.vreg_);
|
||||
break;
|
||||
}
|
||||
CHECK(current_block_ != nullptr);
|
||||
sequence()->EndBlock(current_block_->rpo_number());
|
||||
current_block_ = nullptr;
|
||||
}
|
||||
|
||||
void Allocate() {
|
||||
if (FLAG_trace_alloc) {
|
||||
CHECK_EQ(nullptr, current_block_);
|
||||
CHECK(is_last_block_);
|
||||
WireBlocks();
|
||||
if (FLAG_trace_alloc || FLAG_trace_turbo) {
|
||||
OFStream os(stdout);
|
||||
PrintableInstructionSequence printable = {config(), sequence()};
|
||||
os << "Before: " << std::endl << printable << std::endl;
|
||||
}
|
||||
allocator()->Allocate();
|
||||
if (FLAG_trace_alloc) {
|
||||
if (FLAG_trace_alloc || FLAG_trace_turbo) {
|
||||
OFStream os(stdout);
|
||||
PrintableInstructionSequence printable = {config(), sequence()};
|
||||
os << "After: " << std::endl << printable << std::endl;
|
||||
@ -131,61 +185,223 @@ class RegisterAllocatorTest : public TestWithZone {
|
||||
int NewReg() { return sequence()->NextVirtualRegister(); }
|
||||
|
||||
int Parameter() {
|
||||
// TODO(dcarney): assert parameters before other instructions.
|
||||
int vreg = NewReg();
|
||||
InstructionOperand* outputs[1]{
|
||||
Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)};
|
||||
sequence()->AddInstruction(
|
||||
Instruction::New(zone(), kArchNop, 1, outputs, 0, NULL, 0, NULL));
|
||||
InstructionOperand* outputs[1]{UseRegister(vreg)};
|
||||
Emit(kArchNop, 1, outputs);
|
||||
return vreg;
|
||||
}
|
||||
|
||||
void Return(int vreg) {
|
||||
InstructionOperand* inputs[1]{
|
||||
Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)};
|
||||
sequence()->AddInstruction(
|
||||
Instruction::New(zone(), kArchNop, 0, NULL, 1, inputs, 0, NULL));
|
||||
Instruction* Return(int vreg) {
|
||||
InstructionOperand* inputs[1]{UseRegister(vreg)};
|
||||
return Emit(kArchRet, 0, nullptr, 1, inputs);
|
||||
}
|
||||
|
||||
Instruction* Emit(int output_vreg, int input_vreg_0, int input_vreg_1) {
|
||||
PhiInstruction* Phi(int vreg) {
|
||||
PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg());
|
||||
phi->operands().push_back(vreg);
|
||||
current_block_->AddPhi(phi);
|
||||
return phi;
|
||||
}
|
||||
|
||||
int DefineConstant(int32_t imm = 0) {
|
||||
int virtual_register = NewReg();
|
||||
sequence()->AddConstant(virtual_register, Constant(imm));
|
||||
InstructionOperand* outputs[1]{
|
||||
Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, output_vreg)};
|
||||
InstructionOperand* inputs[2]{
|
||||
Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, input_vreg_0),
|
||||
Unallocated(UnallocatedOperand::MUST_HAVE_REGISTER, input_vreg_1)};
|
||||
Instruction* instruction =
|
||||
Instruction::New(zone(), kArchNop, 1, outputs, 2, inputs, 0, NULL);
|
||||
sequence()->AddInstruction(instruction);
|
||||
return instruction;
|
||||
ConstantOperand::Create(virtual_register, zone())};
|
||||
Emit(kArchNop, 1, outputs);
|
||||
return virtual_register;
|
||||
}
|
||||
|
||||
ImmediateOperand* Immediate(int32_t imm = 0) {
|
||||
int index = sequence()->AddImmediate(Constant(imm));
|
||||
return ImmediateOperand::Create(index, zone());
|
||||
}
|
||||
|
||||
Instruction* EmitFRI(int output_vreg, int input_vreg_0) {
|
||||
InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)};
|
||||
InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Immediate()};
|
||||
return Emit(kArchNop, 1, outputs, 2, inputs);
|
||||
}
|
||||
|
||||
Instruction* EmitFRU(int output_vreg, int input_vreg_0, int input_vreg_1) {
|
||||
InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)};
|
||||
InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Use(input_vreg_1)};
|
||||
return Emit(kArchNop, 1, outputs, 2, inputs);
|
||||
}
|
||||
|
||||
Instruction* EmitRRR(int output_vreg, int input_vreg_0, int input_vreg_1) {
|
||||
InstructionOperand* outputs[1]{UseRegister(output_vreg)};
|
||||
InstructionOperand* inputs[2]{UseRegister(input_vreg_0),
|
||||
UseRegister(input_vreg_1)};
|
||||
return Emit(kArchNop, 1, outputs, 2, inputs);
|
||||
}
|
||||
|
||||
private:
|
||||
InstructionOperand* Unallocated(UnallocatedOperand::ExtendedPolicy policy,
|
||||
int vreg) {
|
||||
UnallocatedOperand* op =
|
||||
new (zone()) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER);
|
||||
InstructionOperand* Unallocated(int vreg,
|
||||
UnallocatedOperand::ExtendedPolicy policy) {
|
||||
UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy);
|
||||
op->set_virtual_register(vreg);
|
||||
return op;
|
||||
}
|
||||
|
||||
int num_general_registers_;
|
||||
int num_double_registers_;
|
||||
InstructionOperand* Unallocated(int vreg,
|
||||
UnallocatedOperand::ExtendedPolicy policy,
|
||||
UnallocatedOperand::Lifetime lifetime) {
|
||||
UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy, lifetime);
|
||||
op->set_virtual_register(vreg);
|
||||
return op;
|
||||
}
|
||||
|
||||
InstructionOperand* UseRegister(int vreg) {
|
||||
return Unallocated(vreg, UnallocatedOperand::MUST_HAVE_REGISTER);
|
||||
}
|
||||
|
||||
InstructionOperand* DefineSameAsFirst(int vreg) {
|
||||
return Unallocated(vreg, UnallocatedOperand::SAME_AS_FIRST_INPUT);
|
||||
}
|
||||
|
||||
InstructionOperand* Use(int vreg) {
|
||||
return Unallocated(vreg, UnallocatedOperand::NONE,
|
||||
UnallocatedOperand::USED_AT_START);
|
||||
}
|
||||
|
||||
void EmitBranch(int vreg) {
|
||||
InstructionOperand* inputs[4]{UseRegister(vreg), Immediate(), Immediate(),
|
||||
Immediate()};
|
||||
InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
|
||||
FlagsConditionField::encode(kEqual);
|
||||
Instruction* instruction =
|
||||
NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl();
|
||||
sequence()->AddInstruction(instruction);
|
||||
}
|
||||
|
||||
void EmitFallThrough() {
|
||||
Instruction* instruction =
|
||||
NewInstruction(kArchNop, 0, nullptr)->MarkAsControl();
|
||||
sequence()->AddInstruction(instruction);
|
||||
}
|
||||
|
||||
void EmitJump() {
|
||||
InstructionOperand* inputs[1]{Immediate()};
|
||||
Instruction* instruction =
|
||||
NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl();
|
||||
sequence()->AddInstruction(instruction);
|
||||
}
|
||||
|
||||
Instruction* NewInstruction(InstructionCode code, size_t outputs_size,
|
||||
InstructionOperand** outputs,
|
||||
size_t inputs_size = 0,
|
||||
InstructionOperand* *inputs = nullptr,
|
||||
size_t temps_size = 0,
|
||||
InstructionOperand* *temps = nullptr) {
|
||||
CHECK_NE(nullptr, current_block_);
|
||||
return Instruction::New(zone(), code, outputs_size, outputs, inputs_size,
|
||||
inputs, temps_size, temps);
|
||||
}
|
||||
|
||||
Instruction* Emit(InstructionCode code, size_t outputs_size,
|
||||
InstructionOperand** outputs, size_t inputs_size = 0,
|
||||
InstructionOperand* *inputs = nullptr,
|
||||
size_t temps_size = 0,
|
||||
InstructionOperand* *temps = nullptr) {
|
||||
Instruction* instruction = NewInstruction(
|
||||
code, outputs_size, outputs, inputs_size, inputs, temps_size, temps);
|
||||
sequence()->AddInstruction(instruction);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
InstructionBlock* NewBlock() {
|
||||
CHECK(current_block_ == nullptr);
|
||||
BasicBlock::Id block_id =
|
||||
BasicBlock::Id::FromSize(instruction_blocks_.size());
|
||||
Rpo rpo = Rpo::FromInt(block_id.ToInt());
|
||||
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()) {
|
||||
loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_);
|
||||
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.
|
||||
InstructionBlock* instruction_block = new (zone()) InstructionBlock(
|
||||
zone(), block_id, rpo, rpo, loop_header, loop_end, false);
|
||||
instruction_blocks_.push_back(instruction_block);
|
||||
current_block_ = instruction_block;
|
||||
sequence()->StartBlock(rpo);
|
||||
return instruction_block;
|
||||
}
|
||||
|
||||
void WireBlocks() {
|
||||
CHECK(instruction_blocks_.size() == completions_.size());
|
||||
size_t offset = 0;
|
||||
size_t size = instruction_blocks_.size();
|
||||
for (const auto& completion : completions_) {
|
||||
switch (completion.type_) {
|
||||
case kFallThrough:
|
||||
if (offset == size - 1) break;
|
||||
// Fallthrough.
|
||||
case kJump:
|
||||
WireBlock(offset, completion.offset_0_);
|
||||
break;
|
||||
case kBranch:
|
||||
WireBlock(offset, completion.offset_0_);
|
||||
WireBlock(offset, completion.offset_1_);
|
||||
break;
|
||||
}
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
void 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());
|
||||
InstructionBlock* block = instruction_blocks_[block_offset];
|
||||
InstructionBlock* target = instruction_blocks_[target_block_offset];
|
||||
block->successors().push_back(target->rpo_number());
|
||||
target->predecessors().push_back(block->rpo_number());
|
||||
}
|
||||
|
||||
struct LoopData {
|
||||
Rpo loop_header_;
|
||||
int expected_blocks_;
|
||||
};
|
||||
typedef std::vector<LoopData> LoopBlocks;
|
||||
typedef std::vector<BlockCompletion> Completions;
|
||||
|
||||
SmartPointer<RegisterConfiguration> config_;
|
||||
ZoneVector<BasicBlock*> basic_blocks_;
|
||||
InstructionBlocks instruction_blocks_;
|
||||
InstructionBlock* current_block_;
|
||||
SmartPointer<Frame> frame_;
|
||||
SmartPointer<RegisterAllocator> allocator_;
|
||||
SmartPointer<InstructionSequence> sequence_;
|
||||
int num_general_registers_;
|
||||
int num_double_registers_;
|
||||
|
||||
// Block building state.
|
||||
InstructionBlocks instruction_blocks_;
|
||||
Completions completions_;
|
||||
LoopBlocks loop_blocks_;
|
||||
InstructionBlock* current_block_;
|
||||
bool is_last_block_;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) {
|
||||
StartBlock();
|
||||
StartLastBlock();
|
||||
int a_reg = Parameter();
|
||||
int b_reg = Parameter();
|
||||
int c_reg = NewReg();
|
||||
Instruction* res = Emit(c_reg, a_reg, b_reg);
|
||||
Instruction* res = EmitRRR(c_reg, a_reg, b_reg);
|
||||
Return(c_reg);
|
||||
EndBlock();
|
||||
|
||||
@ -194,6 +410,49 @@ TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) {
|
||||
ASSERT_TRUE(res->OutputAt(0)->IsRegister());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RegisterAllocatorTest, SimpleLoop) {
|
||||
// i = K;
|
||||
// while(true) { i++ }
|
||||
|
||||
StartBlock();
|
||||
int i_reg = DefineConstant();
|
||||
EndBlock();
|
||||
|
||||
{
|
||||
StartLoop(1);
|
||||
|
||||
StartLastBlock();
|
||||
PhiInstruction* phi = Phi(i_reg);
|
||||
int ipp = NewReg();
|
||||
EmitFRU(ipp, phi->virtual_register(), DefineConstant());
|
||||
phi->operands().push_back(ipp);
|
||||
EndBlock(Jump(0));
|
||||
|
||||
EndLoop();
|
||||
}
|
||||
|
||||
Allocate();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(RegisterAllocatorTest, SimpleBranch) {
|
||||
// return i ? K1 : K2
|
||||
StartBlock();
|
||||
int i_reg = DefineConstant();
|
||||
EndBlock(Branch(i_reg, 1, 2));
|
||||
|
||||
StartBlock();
|
||||
Return(DefineConstant());
|
||||
EndBlock();
|
||||
|
||||
StartLastBlock();
|
||||
Return(DefineConstant());
|
||||
EndBlock();
|
||||
|
||||
Allocate();
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user