| Index: test/unittests/compiler/register-allocator-unittest.cc
 | 
| diff --git a/test/unittests/compiler/register-allocator-unittest.cc b/test/unittests/compiler/register-allocator-unittest.cc
 | 
| index 1c7aeee4fd35dff2bf9f158c7744ceba5b639564..b1597c186511b8a4a63616e54fa2d5815fbd2dcf 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 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
 | 
| 
 |