| 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 db457c0d65a19cc00eb9837cf3ae1ae54bd12bf6..074789bae4ecc92fe8e39a000ec84f4d689796ad 100644
|
| --- a/test/unittests/compiler/register-allocator-unittest.cc
|
| +++ b/test/unittests/compiler/register-allocator-unittest.cc
|
| @@ -14,8 +14,6 @@ namespace compiler {
|
|
|
| typedef BasicBlock::RpoNumber Rpo;
|
|
|
| -namespace {
|
| -
|
| static const char*
|
| general_register_names_[RegisterConfiguration::kMaxGeneralRegisters];
|
| static const char*
|
| @@ -23,7 +21,6 @@ static const char*
|
| static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters +
|
| RegisterConfiguration::kMaxDoubleRegisters)];
|
|
|
| -
|
| static void InitializeRegisterNames() {
|
| char* loc = register_names_;
|
| for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) {
|
| @@ -38,52 +35,107 @@ static void InitializeRegisterNames() {
|
| }
|
| }
|
|
|
| -enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump };
|
|
|
| -struct BlockCompletion {
|
| - BlockCompletionType type_;
|
| - int vreg_;
|
| - int offset_0_;
|
| - int offset_1_;
|
| -};
|
| +class RegisterAllocatorTest : public TestWithZone {
|
| + public:
|
| + static const int kDefaultNRegs = 4;
|
| + static const int kNoValue = kMinInt;
|
|
|
| -static const int kInvalidJumpOffset = kMinInt;
|
| + struct VReg {
|
| + VReg() : value_(kNoValue) {}
|
| + VReg(PhiInstruction* phi) : value_(phi->virtual_register()) {} // NOLINT
|
| + explicit VReg(int value) : value_(value) {}
|
| + int value_;
|
| + };
|
|
|
| -BlockCompletion FallThrough() {
|
| - BlockCompletion completion = {kFallThrough, -1, 1, kInvalidJumpOffset};
|
| - return completion;
|
| -}
|
| + enum TestOperandType {
|
| + kInvalid,
|
| + kSameAsFirst,
|
| + kRegister,
|
| + kFixedRegister,
|
| + kSlot,
|
| + kFixedSlot,
|
| + kImmediate,
|
| + kNone
|
| + };
|
|
|
| + struct TestOperand {
|
| + TestOperand() : type_(kInvalid), vreg_(), value_(kNoValue) {}
|
| + TestOperand(TestOperandType type, int imm)
|
| + : type_(type), vreg_(), value_(imm) {}
|
| + TestOperand(TestOperandType type, VReg vreg, int value = kNoValue)
|
| + : type_(type), vreg_(vreg), value_(value) {}
|
|
|
| -BlockCompletion Jump(int offset) {
|
| - BlockCompletion completion = {kJump, -1, offset, kInvalidJumpOffset};
|
| - return completion;
|
| -}
|
| + TestOperandType type_;
|
| + VReg vreg_;
|
| + int value_;
|
| + };
|
|
|
| + static TestOperand Same() { return TestOperand(kSameAsFirst, VReg()); }
|
|
|
| -BlockCompletion Branch(int vreg, int left_offset, int right_offset) {
|
| - BlockCompletion completion = {kBranch, vreg, left_offset, right_offset};
|
| - return completion;
|
| -}
|
| + static TestOperand Reg(VReg vreg, int index = kNoValue) {
|
| + TestOperandType type = kRegister;
|
| + if (index != kNoValue) type = kFixedRegister;
|
| + return TestOperand(type, vreg, index);
|
| + }
|
|
|
| -} // namespace
|
| + static TestOperand Reg(int index = kNoValue) { return Reg(VReg(), index); }
|
|
|
| + static TestOperand Slot(VReg vreg, int index = kNoValue) {
|
| + TestOperandType type = kSlot;
|
| + if (index != kNoValue) type = kFixedSlot;
|
| + return TestOperand(type, vreg, index);
|
| + }
|
|
|
| -class RegisterAllocatorTest : public TestWithZone {
|
| - public:
|
| - static const int kDefaultNRegs = 4;
|
| + static TestOperand Slot(int index = kNoValue) { return Slot(VReg(), index); }
|
| +
|
| + static TestOperand Use(VReg vreg) { return TestOperand(kNone, vreg); }
|
| +
|
| + static TestOperand Use() { return Use(VReg()); }
|
| +
|
| + enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump };
|
| +
|
| + struct BlockCompletion {
|
| + BlockCompletionType type_;
|
| + TestOperand op_;
|
| + int offset_0_;
|
| + int offset_1_;
|
| + };
|
| +
|
| + static BlockCompletion FallThrough() {
|
| + BlockCompletion completion = {kFallThrough, TestOperand(), 1, kNoValue};
|
| + return completion;
|
| + }
|
| +
|
| + static BlockCompletion Jump(int offset) {
|
| + BlockCompletion completion = {kJump, TestOperand(), offset, kNoValue};
|
| + return completion;
|
| + }
|
| +
|
| + static BlockCompletion Branch(TestOperand op, int left_offset,
|
| + int right_offset) {
|
| + BlockCompletion completion = {kBranch, op, left_offset, right_offset};
|
| + return completion;
|
| + }
|
| +
|
| + static BlockCompletion Last() {
|
| + BlockCompletion completion = {kBlockEnd, TestOperand(), kNoValue, kNoValue};
|
| + return completion;
|
| + }
|
|
|
| RegisterAllocatorTest()
|
| : num_general_registers_(kDefaultNRegs),
|
| num_double_registers_(kDefaultNRegs),
|
| instruction_blocks_(zone()),
|
| + current_instruction_index_(-1),
|
| current_block_(nullptr),
|
| - is_last_block_(false),
|
| block_returns_(false) {
|
| InitializeRegisterNames();
|
| }
|
|
|
| void SetNumRegs(int num_general_registers, int num_double_registers) {
|
| + CHECK(config_.is_empty());
|
| + CHECK(instructions_.empty());
|
| CHECK(instruction_blocks_.empty());
|
| num_general_registers_ = num_general_registers;
|
| num_double_registers_ = num_double_registers;
|
| @@ -136,48 +188,41 @@ class RegisterAllocatorTest : public TestWithZone {
|
| loop_blocks_.pop_back();
|
| }
|
|
|
| - void StartLastBlock() {
|
| - CHECK(!is_last_block_);
|
| - is_last_block_ = true;
|
| - NewBlock();
|
| - }
|
| -
|
| void StartBlock() {
|
| - CHECK(!is_last_block_);
|
| block_returns_ = false;
|
| NewBlock();
|
| }
|
|
|
| - void EndBlock(BlockCompletion completion = FallThrough()) {
|
| - completions_.push_back(completion);
|
| + int EndBlock(BlockCompletion completion = FallThrough()) {
|
| + int instruction_index = kMinInt;
|
| + if (block_returns_) {
|
| + CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough);
|
| + completion.type_ = kBlockEnd;
|
| + }
|
| switch (completion.type_) {
|
| case kBlockEnd:
|
| - CHECK(false); // unreachable;
|
| break;
|
| case kFallThrough:
|
| - if (is_last_block_ || block_returns_) {
|
| - completions_.back().type_ = kBlockEnd;
|
| - break;
|
| - }
|
| - EmitFallThrough();
|
| + instruction_index = EmitFallThrough();
|
| break;
|
| case kJump:
|
| CHECK(!block_returns_);
|
| - EmitJump();
|
| + instruction_index = EmitJump();
|
| break;
|
| case kBranch:
|
| CHECK(!block_returns_);
|
| - EmitBranch(completion.vreg_);
|
| + instruction_index = EmitBranch(completion.op_);
|
| break;
|
| }
|
| + completions_.push_back(completion);
|
| CHECK(current_block_ != nullptr);
|
| sequence()->EndBlock(current_block_->rpo_number());
|
| current_block_ = nullptr;
|
| + return instruction_index;
|
| }
|
|
|
| void Allocate() {
|
| CHECK_EQ(nullptr, current_block_);
|
| - CHECK(is_last_block_);
|
| WireBlocks();
|
| RegisterAllocatorVerifier verifier(zone(), config(), sequence());
|
| if (FLAG_trace_alloc || FLAG_trace_turbo) {
|
| @@ -195,111 +240,110 @@ class RegisterAllocatorTest : public TestWithZone {
|
| verifier.VerifyGapMoves();
|
| }
|
|
|
| - int NewReg() { return sequence()->NextVirtualRegister(); }
|
| + TestOperand Imm(int32_t imm = 0) {
|
| + int index = sequence()->AddImmediate(Constant(imm));
|
| + return TestOperand(kImmediate, index);
|
| + }
|
|
|
| - int Parameter() {
|
| - int vreg = NewReg();
|
| - InstructionOperand* outputs[1]{UseRegister(vreg)};
|
| - Emit(kArchNop, 1, outputs);
|
| + VReg Parameter(TestOperand output_op = Reg()) {
|
| + VReg vreg = NewReg();
|
| + InstructionOperand* outputs[1]{ConvertOutputOp(vreg, output_op)};
|
| + Emit(vreg.value_, kArchNop, 1, outputs);
|
| return vreg;
|
| }
|
|
|
| - Instruction* Return(int vreg) {
|
| + int Return(TestOperand input_op_0) {
|
| block_returns_ = true;
|
| - InstructionOperand* inputs[1]{UseRegister(vreg)};
|
| - return Emit(kArchRet, 0, nullptr, 1, inputs);
|
| + InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)};
|
| + return Emit(NewIndex(), kArchRet, 0, nullptr, 1, inputs);
|
| }
|
|
|
| - PhiInstruction* Phi(int vreg) {
|
| - PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg());
|
| - phi->operands().push_back(vreg);
|
| + int Return(VReg vreg) { return Return(Reg(vreg, 0)); }
|
| +
|
| + PhiInstruction* Phi(VReg incoming_vreg) {
|
| + PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg().value_);
|
| + phi->operands().push_back(incoming_vreg.value_);
|
| 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]{
|
| - 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);
|
| + PhiInstruction* Phi(VReg incoming_vreg_0, VReg incoming_vreg_1) {
|
| + auto phi = Phi(incoming_vreg_0);
|
| + Extend(phi, incoming_vreg_1);
|
| + return phi;
|
| }
|
|
|
| - 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);
|
| + static void Extend(PhiInstruction* phi, VReg vreg) {
|
| + phi->operands().push_back(vreg.value_);
|
| }
|
|
|
| - 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);
|
| + VReg DefineConstant(int32_t imm = 0) {
|
| + VReg vreg = NewReg();
|
| + sequence()->AddConstant(vreg.value_, Constant(imm));
|
| + InstructionOperand* outputs[1]{
|
| + ConstantOperand::Create(vreg.value_, zone())};
|
| + Emit(vreg.value_, kArchNop, 1, outputs);
|
| + return vreg;
|
| }
|
|
|
| - private:
|
| - InstructionOperand* Unallocated(int vreg,
|
| - UnallocatedOperand::ExtendedPolicy policy) {
|
| - UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy);
|
| - op->set_virtual_register(vreg);
|
| - return op;
|
| + VReg EmitOII(TestOperand output_op, TestOperand input_op_0,
|
| + TestOperand input_op_1) {
|
| + VReg output_vreg = NewReg();
|
| + InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)};
|
| + InstructionOperand* inputs[2]{ConvertInputOp(input_op_0),
|
| + ConvertInputOp(input_op_1)};
|
| + Emit(output_vreg.value_, kArchNop, 1, outputs, 2, inputs);
|
| + return output_vreg;
|
| }
|
|
|
| - InstructionOperand* Unallocated(int vreg,
|
| - UnallocatedOperand::ExtendedPolicy policy,
|
| - UnallocatedOperand::Lifetime lifetime) {
|
| - UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy, lifetime);
|
| - op->set_virtual_register(vreg);
|
| - return op;
|
| + VReg EmitCall(TestOperand output_op, size_t input_size, TestOperand* inputs) {
|
| + VReg output_vreg = NewReg();
|
| + InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)};
|
| + InstructionOperand** mapped_inputs =
|
| + zone()->NewArray<InstructionOperand*>(static_cast<int>(input_size));
|
| + for (size_t i = 0; i < input_size; ++i) {
|
| + mapped_inputs[i] = ConvertInputOp(inputs[i]);
|
| + }
|
| + Emit(output_vreg.value_, kArchCallCodeObject, 1, outputs, input_size,
|
| + mapped_inputs);
|
| + return output_vreg;
|
| }
|
|
|
| - InstructionOperand* UseRegister(int vreg) {
|
| - return Unallocated(vreg, UnallocatedOperand::MUST_HAVE_REGISTER);
|
| + // Get defining instruction vreg or value returned at instruction creation
|
| + // time when there is no return value.
|
| + const Instruction* GetInstruction(int instruction_index) {
|
| + auto it = instructions_.find(instruction_index);
|
| + CHECK(it != instructions_.end());
|
| + return it->second;
|
| }
|
|
|
| - InstructionOperand* DefineSameAsFirst(int vreg) {
|
| - return Unallocated(vreg, UnallocatedOperand::SAME_AS_FIRST_INPUT);
|
| - }
|
| + private:
|
| + VReg NewReg() { return VReg(sequence()->NextVirtualRegister()); }
|
| + int NewIndex() { return current_instruction_index_--; }
|
|
|
| - InstructionOperand* Use(int vreg) {
|
| - return Unallocated(vreg, UnallocatedOperand::NONE,
|
| - UnallocatedOperand::USED_AT_START);
|
| - }
|
| + static TestOperand Invalid() { return TestOperand(kInvalid, VReg()); }
|
|
|
| - void EmitBranch(int vreg) {
|
| - InstructionOperand* inputs[4]{UseRegister(vreg), Immediate(), Immediate(),
|
| - Immediate()};
|
| + int EmitBranch(TestOperand input_op) {
|
| + InstructionOperand* inputs[4]{ConvertInputOp(input_op),
|
| + ConvertInputOp(Imm()), ConvertInputOp(Imm()),
|
| + ConvertInputOp(Imm())};
|
| InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
|
| FlagsConditionField::encode(kEqual);
|
| - Instruction* instruction =
|
| + auto instruction =
|
| NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl();
|
| - sequence()->AddInstruction(instruction);
|
| + return AddInstruction(NewIndex(), instruction);
|
| }
|
|
|
| - void EmitFallThrough() {
|
| - Instruction* instruction =
|
| - NewInstruction(kArchNop, 0, nullptr)->MarkAsControl();
|
| - sequence()->AddInstruction(instruction);
|
| + int EmitFallThrough() {
|
| + auto instruction = NewInstruction(kArchNop, 0, nullptr)->MarkAsControl();
|
| + return AddInstruction(NewIndex(), instruction);
|
| }
|
|
|
| - void EmitJump() {
|
| - InstructionOperand* inputs[1]{Immediate()};
|
| - Instruction* instruction =
|
| + int EmitJump() {
|
| + InstructionOperand* inputs[1]{ConvertInputOp(Imm())};
|
| + auto instruction =
|
| NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl();
|
| - sequence()->AddInstruction(instruction);
|
| + return AddInstruction(NewIndex(), instruction);
|
| }
|
|
|
| Instruction* NewInstruction(InstructionCode code, size_t outputs_size,
|
| @@ -313,21 +357,83 @@ class RegisterAllocatorTest : public TestWithZone {
|
| 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;
|
| + InstructionOperand* Unallocated(TestOperand op,
|
| + UnallocatedOperand::ExtendedPolicy policy) {
|
| + auto unallocated = new (zone()) UnallocatedOperand(policy);
|
| + unallocated->set_virtual_register(op.vreg_.value_);
|
| + return unallocated;
|
| + }
|
| +
|
| + InstructionOperand* Unallocated(TestOperand op,
|
| + UnallocatedOperand::ExtendedPolicy policy,
|
| + UnallocatedOperand::Lifetime lifetime) {
|
| + auto unallocated = new (zone()) UnallocatedOperand(policy, lifetime);
|
| + unallocated->set_virtual_register(op.vreg_.value_);
|
| + return unallocated;
|
| + }
|
| +
|
| + InstructionOperand* Unallocated(TestOperand op,
|
| + UnallocatedOperand::ExtendedPolicy policy,
|
| + int index) {
|
| + auto unallocated = new (zone()) UnallocatedOperand(policy, index);
|
| + unallocated->set_virtual_register(op.vreg_.value_);
|
| + return unallocated;
|
| + }
|
| +
|
| + InstructionOperand* Unallocated(TestOperand op,
|
| + UnallocatedOperand::BasicPolicy policy,
|
| + int index) {
|
| + auto unallocated = new (zone()) UnallocatedOperand(policy, index);
|
| + unallocated->set_virtual_register(op.vreg_.value_);
|
| + return unallocated;
|
| + }
|
| +
|
| + InstructionOperand* ConvertInputOp(TestOperand op) {
|
| + if (op.type_ == kImmediate) {
|
| + CHECK_EQ(op.vreg_.value_, kNoValue);
|
| + return ImmediateOperand::Create(op.value_, zone());
|
| + }
|
| + CHECK_NE(op.vreg_.value_, kNoValue);
|
| + switch (op.type_) {
|
| + case kNone:
|
| + return Unallocated(op, UnallocatedOperand::NONE,
|
| + UnallocatedOperand::USED_AT_START);
|
| + case kRegister:
|
| + return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER,
|
| + UnallocatedOperand::USED_AT_START);
|
| + case kFixedRegister:
|
| + CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
|
| + return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
|
| + default:
|
| + break;
|
| + }
|
| + CHECK(false);
|
| + return NULL;
|
| + }
|
| +
|
| + InstructionOperand* ConvertOutputOp(VReg vreg, TestOperand op) {
|
| + CHECK_EQ(op.vreg_.value_, kNoValue);
|
| + op.vreg_ = vreg;
|
| + switch (op.type_) {
|
| + case kSameAsFirst:
|
| + return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT);
|
| + case kRegister:
|
| + return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
|
| + case kFixedSlot:
|
| + return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
|
| + case kFixedRegister:
|
| + CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
|
| + return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
|
| + default:
|
| + break;
|
| + }
|
| + CHECK(false);
|
| + return NULL;
|
| }
|
|
|
| InstructionBlock* NewBlock() {
|
| CHECK(current_block_ == nullptr);
|
| - BasicBlock::Id block_id =
|
| - BasicBlock::Id::FromSize(instruction_blocks_.size());
|
| + auto 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();
|
| @@ -347,7 +453,7 @@ class RegisterAllocatorTest : public TestWithZone {
|
| }
|
| }
|
| // Construct instruction block.
|
| - InstructionBlock* instruction_block = new (zone()) InstructionBlock(
|
| + auto 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;
|
| @@ -380,17 +486,33 @@ class RegisterAllocatorTest : public TestWithZone {
|
| 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];
|
| + auto block = instruction_blocks_[block_offset];
|
| + auto target = instruction_blocks_[target_block_offset];
|
| block->successors().push_back(target->rpo_number());
|
| target->predecessors().push_back(block->rpo_number());
|
| }
|
|
|
| + int Emit(int instruction_index, InstructionCode code, size_t outputs_size,
|
| + InstructionOperand** outputs, size_t inputs_size = 0,
|
| + InstructionOperand* *inputs = nullptr, size_t temps_size = 0,
|
| + InstructionOperand* *temps = nullptr) {
|
| + auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size,
|
| + inputs, temps_size, temps);
|
| + return AddInstruction(instruction_index, instruction);
|
| + }
|
| +
|
| + int AddInstruction(int instruction_index, Instruction* instruction) {
|
| + sequence()->AddInstruction(instruction);
|
| + return instruction_index;
|
| + }
|
| +
|
| struct LoopData {
|
| Rpo loop_header_;
|
| int expected_blocks_;
|
| };
|
| +
|
| typedef std::vector<LoopData> LoopBlocks;
|
| + typedef std::map<int, const Instruction*> Instructions;
|
| typedef std::vector<BlockCompletion> Completions;
|
|
|
| SmartPointer<RegisterConfiguration> config_;
|
| @@ -402,45 +524,42 @@ class RegisterAllocatorTest : public TestWithZone {
|
|
|
| // Block building state.
|
| InstructionBlocks instruction_blocks_;
|
| + Instructions instructions_;
|
| + int current_instruction_index_;
|
| Completions completions_;
|
| LoopBlocks loop_blocks_;
|
| InstructionBlock* current_block_;
|
| - bool is_last_block_;
|
| bool block_returns_;
|
| };
|
|
|
|
|
| TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) {
|
| - StartLastBlock();
|
| - int a_reg = Parameter();
|
| - int b_reg = Parameter();
|
| - int c_reg = NewReg();
|
| - Instruction* res = EmitRRR(c_reg, a_reg, b_reg);
|
| + // return p0 + p1;
|
| + StartBlock();
|
| + auto a_reg = Parameter();
|
| + auto b_reg = Parameter();
|
| + auto c_reg = EmitOII(Reg(1), Reg(a_reg, 1), Reg(b_reg, 0));
|
| Return(c_reg);
|
| - EndBlock();
|
| + EndBlock(Last());
|
|
|
| Allocate();
|
| -
|
| - ASSERT_TRUE(res->OutputAt(0)->IsRegister());
|
| }
|
|
|
|
|
| TEST_F(RegisterAllocatorTest, SimpleLoop) {
|
| // i = K;
|
| // while(true) { i++ }
|
| -
|
| StartBlock();
|
| - int i_reg = DefineConstant();
|
| + auto 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);
|
| + StartBlock();
|
| + auto phi = Phi(i_reg);
|
| + auto ipp = EmitOII(Same(), Reg(phi), Use(DefineConstant()));
|
| + Extend(phi, ipp);
|
| EndBlock(Jump(0));
|
|
|
| EndLoop();
|
| @@ -453,15 +572,127 @@ TEST_F(RegisterAllocatorTest, SimpleLoop) {
|
| TEST_F(RegisterAllocatorTest, SimpleBranch) {
|
| // return i ? K1 : K2
|
| StartBlock();
|
| - int i_reg = DefineConstant();
|
| - EndBlock(Branch(i_reg, 1, 2));
|
| + auto i = DefineConstant();
|
| + EndBlock(Branch(Reg(i), 1, 2));
|
|
|
| StartBlock();
|
| Return(DefineConstant());
|
| - EndBlock();
|
| + EndBlock(Last());
|
|
|
| - StartLastBlock();
|
| + StartBlock();
|
| Return(DefineConstant());
|
| + EndBlock(Last());
|
| +
|
| + Allocate();
|
| +}
|
| +
|
| +
|
| +TEST_F(RegisterAllocatorTest, SimpleDiamond) {
|
| + // return p0 ? p0 : p0
|
| + StartBlock();
|
| + auto param = Parameter();
|
| + EndBlock(Branch(Reg(param), 1, 2));
|
| +
|
| + StartBlock();
|
| + EndBlock(Jump(2));
|
| +
|
| + StartBlock();
|
| + EndBlock(Jump(1));
|
| +
|
| + StartBlock();
|
| + Return(param);
|
| + EndBlock();
|
| +
|
| + Allocate();
|
| +}
|
| +
|
| +
|
| +TEST_F(RegisterAllocatorTest, SimpleDiamondPhi) {
|
| + // return i ? K1 : K2
|
| + StartBlock();
|
| + EndBlock(Branch(Reg(DefineConstant()), 1, 2));
|
| +
|
| + StartBlock();
|
| + auto t_val = DefineConstant();
|
| + EndBlock(Jump(2));
|
| +
|
| + StartBlock();
|
| + auto f_val = DefineConstant();
|
| + EndBlock(Jump(1));
|
| +
|
| + StartBlock();
|
| + Return(Reg(Phi(t_val, f_val)));
|
| + EndBlock();
|
| +
|
| + Allocate();
|
| +}
|
| +
|
| +
|
| +TEST_F(RegisterAllocatorTest, DiamondManyPhis) {
|
| + const int kPhis = kDefaultNRegs * 2;
|
| +
|
| + StartBlock();
|
| + EndBlock(Branch(Reg(DefineConstant()), 1, 2));
|
| +
|
| + StartBlock();
|
| + VReg t_vals[kPhis];
|
| + for (int i = 0; i < kPhis; ++i) {
|
| + t_vals[i] = DefineConstant();
|
| + }
|
| + EndBlock(Jump(2));
|
| +
|
| + StartBlock();
|
| + VReg f_vals[kPhis];
|
| + for (int i = 0; i < kPhis; ++i) {
|
| + f_vals[i] = DefineConstant();
|
| + }
|
| + EndBlock(Jump(1));
|
| +
|
| + StartBlock();
|
| + TestOperand merged[kPhis];
|
| + for (int i = 0; i < kPhis; ++i) {
|
| + merged[i] = Use(Phi(t_vals[i], f_vals[i]));
|
| + }
|
| + Return(EmitCall(Reg(), kPhis, merged));
|
| + EndBlock();
|
| +
|
| + Allocate();
|
| +}
|
| +
|
| +
|
| +TEST_F(RegisterAllocatorTest, DoubleDiamondManyRedundantPhis) {
|
| + const int kPhis = kDefaultNRegs * 2;
|
| +
|
| + // First diamond.
|
| + StartBlock();
|
| + VReg vals[kPhis];
|
| + for (int i = 0; i < kPhis; ++i) {
|
| + vals[i] = Parameter(Slot(i));
|
| + }
|
| + EndBlock(Branch(Reg(DefineConstant()), 1, 2));
|
| +
|
| + StartBlock();
|
| + EndBlock(Jump(2));
|
| +
|
| + StartBlock();
|
| + EndBlock(Jump(1));
|
| +
|
| + // Second diamond.
|
| + StartBlock();
|
| + EndBlock(Branch(Reg(DefineConstant()), 1, 2));
|
| +
|
| + StartBlock();
|
| + EndBlock(Jump(2));
|
| +
|
| + StartBlock();
|
| + EndBlock(Jump(1));
|
| +
|
| + StartBlock();
|
| + TestOperand merged[kPhis];
|
| + for (int i = 0; i < kPhis; ++i) {
|
| + merged[i] = Use(Phi(vals[i], vals[i]));
|
| + }
|
| + Return(EmitCall(Reg(), kPhis, merged));
|
| EndBlock();
|
|
|
| Allocate();
|
| @@ -471,14 +702,12 @@ TEST_F(RegisterAllocatorTest, SimpleBranch) {
|
| TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) {
|
| const size_t kNumRegs = 3;
|
| const size_t kParams = kNumRegs + 1;
|
| - int parameters[kParams];
|
| -
|
| // Override number of registers.
|
| SetNumRegs(kNumRegs, kNumRegs);
|
|
|
| - // Initial block.
|
| StartBlock();
|
| - int constant = DefineConstant();
|
| + auto constant = DefineConstant();
|
| + VReg parameters[kParams];
|
| for (size_t i = 0; i < arraysize(parameters); ++i) {
|
| parameters[i] = DefineConstant();
|
| }
|
| @@ -498,12 +727,11 @@ TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) {
|
| // Perform some computations.
|
| // something like phi[i] += const
|
| for (size_t i = 0; i < arraysize(parameters); ++i) {
|
| - int result = NewReg();
|
| - EmitFRU(result, phis[i]->virtual_register(), constant);
|
| - phis[i]->operands().push_back(result);
|
| + auto result = EmitOII(Same(), Reg(phis[i]), Use(constant));
|
| + Extend(phis[i], result);
|
| }
|
|
|
| - EndBlock(Branch(DefineConstant(), 1, 2));
|
| + EndBlock(Branch(Reg(DefineConstant()), 1, 2));
|
|
|
| // Jump back to loop header.
|
| StartBlock();
|
| @@ -512,10 +740,7 @@ TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) {
|
| EndLoop();
|
| }
|
|
|
| - // End block.
|
| - StartLastBlock();
|
| -
|
| - // Return sum.
|
| + StartBlock();
|
| Return(DefineConstant());
|
| EndBlock();
|
|
|
|
|