}
+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();
}
-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;
}
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;
}
}
-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);
}
}
}
+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;
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(
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;
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();
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