[turbofan] extend register allocator testing with control flow
authordcarney@chromium.org <dcarney@chromium.org>
Thu, 6 Nov 2014 08:28:15 +0000 (08:28 +0000)
committerdcarney@chromium.org <dcarney@chromium.org>
Thu, 6 Nov 2014 08:28:45 +0000 (08:28 +0000)
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
src/compiler/instruction.cc
src/compiler/instruction.h
test/cctest/compiler/test-instruction.cc
test/unittests/compiler/register-allocator-unittest.cc

index ffbf2a3..e601b2c 100644 (file)
@@ -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());
   }
 }
 
index ad7a5ee..218130c 100644 (file)
@@ -338,6 +338,36 @@ std::ostream& operator<<(std::ostream& os, const Constant& constant) {
 }
 
 
+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_(id),
+      ao_number_(ao_number),
+      rpo_number_(rpo_number),
+      loop_header_(loop_header),
+      loop_end_(loop_end),
+      code_start_(-1),
+      code_end_(-1),
+      deferred_(deferred) {}
+
+
+size_t InstructionBlock::PredecessorIndexOf(
+    BasicBlock::RpoNumber rpo_number) const {
+  size_t j = 0;
+  for (InstructionBlock::Predecessors::const_iterator i = predecessors_.begin();
+       i != predecessors_.end(); ++i, ++j) {
+    if (*i == rpo_number) break;
+  }
+  return j;
+}
+
+
 static BasicBlock::RpoNumber GetRpo(BasicBlock* block) {
   if (block == NULL) return BasicBlock::RpoNumber::Invalid();
   return block->GetRpoNumber();
@@ -350,43 +380,23 @@ static BasicBlock::RpoNumber GetLoopEndRpo(const BasicBlock* block) {
 }
 
 
-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),
-      phis_(zone),
-      id_(block->id()),
-      ao_number_(block->GetAoNumber()),
-      rpo_number_(block->GetRpoNumber()),
-      loop_header_(GetRpo(block->loop_header())),
-      loop_end_(GetLoopEndRpo(block)),
-      code_start_(-1),
-      code_end_(-1),
-      deferred_(block->deferred()) {
+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
-  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();
+  instr_block->successors().reserve(block->SuccessorCount());
+  for (auto it = block->successors_begin(); it != block->successors_end();
+       ++it) {
+    instr_block->successors().push_back((*it)->GetRpoNumber());
   }
-}
-
-
-size_t InstructionBlock::PredecessorIndexOf(
-    BasicBlock::RpoNumber rpo_number) const {
-  size_t j = 0;
-  for (InstructionBlock::Predecessors::const_iterator i = predecessors_.begin();
-       i != predecessors_.end(); ++i, ++j) {
-    if (*i == rpo_number) break;
+  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 j;
+  return instr_block;
 }
 
 
@@ -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);
 }
index b42e17b..426d4cd 100644 (file)
@@ -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_);
index e0fa346..425a46c 100644 (file)
@@ -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());
 
index 1c7aeee..b1597c1 100644 (file)
@@ -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 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() {
-    CHECK(current_block_ != NULL);
-    sequence()->EndBlock(basic_blocks_[current_block_->rpo_number().ToSize()]);
-    current_block_ = NULL;
+  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);
+  }
+
+  PhiInstruction* Phi(int vreg) {
+    PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg());
+    phi->operands().push_back(vreg);
+    current_block_->AddPhi(phi);
+    return phi;
   }
 
-  Instruction* Emit(int output_vreg, int input_vreg_0, int input_vreg_1) {
+  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