From 57b42dc51f5ee292897f24ba7fdcc955824331cd Mon Sep 17 00:00:00 2001 From: "dcarney@chromium.org" Date: Thu, 6 Nov 2014 08:28:15 +0000 Subject: [PATCH] [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 --- src/compiler/instruction-selector.cc | 4 +- src/compiler/instruction.cc | 94 +++-- src/compiler/instruction.h | 21 +- test/cctest/compiler/test-instruction.cc | 32 +- .../compiler/register-allocator-unittest.cc | 377 +++++++++++++++--- 5 files changed, 401 insertions(+), 127 deletions(-) diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index ffbf2a3013..e601b2c092 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -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()); } } diff --git a/src/compiler/instruction.cc b/src/compiler/instruction.cc index ad7a5ee05c..218130c686 100644 --- a/src/compiler/instruction.cc +++ b/src/compiler/instruction.cc @@ -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(block->SuccessorCount()), - BasicBlock::RpoNumber::Invalid(), zone), - predecessors_(static_cast(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(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(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(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); } diff --git a/src/compiler/instruction.h b/src/compiler/instruction.h index b42e17ba72..426d4cd28d 100644 --- a/src/compiler/instruction.h +++ b/src/compiler/instruction.h @@ -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_); diff --git a/test/cctest/compiler/test-instruction.cc b/test/cctest/compiler/test-instruction.cc index e0fa346408..425a46ced0 100644 --- a/test/cctest/compiler/test-instruction.cc +++ b/test/cctest/compiler/test-instruction.cc @@ -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()); diff --git a/test/unittests/compiler/register-allocator-unittest.cc b/test/unittests/compiler/register-allocator-unittest.cc index 1c7aeee4fd..b1597c1865 100644 --- a/test/unittests/compiler/register-allocator-unittest.cc +++ b/test/unittests/compiler/register-allocator-unittest.cc @@ -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(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 LoopBlocks; + typedef std::vector Completions; + SmartPointer config_; - ZoneVector basic_blocks_; - InstructionBlocks instruction_blocks_; - InstructionBlock* current_block_; SmartPointer frame_; SmartPointer allocator_; SmartPointer 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