Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(635)

Unified Diff: test/unittests/compiler/register-allocator-unittest.cc

Issue 699083003: [turbofan] extend register allocator testing with control flow (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « test/cctest/compiler/test-instruction.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « test/cctest/compiler/test-instruction.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698