 Chromium Code Reviews
 Chromium Code Reviews Issue 700753003:
  [turbofan] add some registerallocator unittests  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
    
  
    Issue 700753003:
  [turbofan] add some registerallocator unittests  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge| OLD | NEW | 
|---|---|
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "src/base/utils/random-number-generator.h" | 5 #include "src/base/utils/random-number-generator.h" | 
| 6 #include "src/compiler/register-allocator.h" | 6 #include "src/compiler/register-allocator.h" | 
| 7 #include "src/compiler/register-allocator-verifier.h" | 7 #include "src/compiler/register-allocator-verifier.h" | 
| 8 #include "test/unittests/test-utils.h" | 8 #include "test/unittests/test-utils.h" | 
| 9 #include "testing/gmock/include/gmock/gmock.h" | 9 #include "testing/gmock/include/gmock/gmock.h" | 
| 10 | 10 | 
| 11 namespace v8 { | 11 namespace v8 { | 
| 12 namespace internal { | 12 namespace internal { | 
| 13 namespace compiler { | 13 namespace compiler { | 
| 14 | 14 | 
| 15 typedef BasicBlock::RpoNumber Rpo; | 15 typedef BasicBlock::RpoNumber Rpo; | 
| 16 | 16 | 
| 17 namespace { | |
| 18 | |
| 19 static const char* | 17 static const char* | 
| 20 general_register_names_[RegisterConfiguration::kMaxGeneralRegisters]; | 18 general_register_names_[RegisterConfiguration::kMaxGeneralRegisters]; | 
| 21 static const char* | 19 static const char* | 
| 22 double_register_names_[RegisterConfiguration::kMaxDoubleRegisters]; | 20 double_register_names_[RegisterConfiguration::kMaxDoubleRegisters]; | 
| 23 static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters + | 21 static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters + | 
| 24 RegisterConfiguration::kMaxDoubleRegisters)]; | 22 RegisterConfiguration::kMaxDoubleRegisters)]; | 
| 25 | 23 | 
| 26 | |
| 27 static void InitializeRegisterNames() { | 24 static void InitializeRegisterNames() { | 
| 28 char* loc = register_names_; | 25 char* loc = register_names_; | 
| 29 for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) { | 26 for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) { | 
| 30 general_register_names_[i] = loc; | 27 general_register_names_[i] = loc; | 
| 31 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); | 28 loc += base::OS::SNPrintF(loc, 100, "gp_%d", i); | 
| 32 *loc++ = 0; | 29 *loc++ = 0; | 
| 33 } | 30 } | 
| 34 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { | 31 for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) { | 
| 35 double_register_names_[i] = loc; | 32 double_register_names_[i] = loc; | 
| 36 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; | 33 loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1; | 
| 37 *loc++ = 0; | 34 *loc++ = 0; | 
| 38 } | 35 } | 
| 39 } | 36 } | 
| 40 | 37 | 
| 41 enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump }; | |
| 42 | |
| 43 struct BlockCompletion { | |
| 44 BlockCompletionType type_; | |
| 45 int vreg_; | |
| 46 int offset_0_; | |
| 47 int offset_1_; | |
| 48 }; | |
| 49 | |
| 50 static const int kInvalidJumpOffset = kMinInt; | |
| 51 | |
| 52 BlockCompletion FallThrough() { | |
| 53 BlockCompletion completion = {kFallThrough, -1, 1, kInvalidJumpOffset}; | |
| 54 return completion; | |
| 55 } | |
| 56 | |
| 57 | |
| 58 BlockCompletion Jump(int offset) { | |
| 59 BlockCompletion completion = {kJump, -1, offset, kInvalidJumpOffset}; | |
| 60 return completion; | |
| 61 } | |
| 62 | |
| 63 | |
| 64 BlockCompletion Branch(int vreg, int left_offset, int right_offset) { | |
| 65 BlockCompletion completion = {kBranch, vreg, left_offset, right_offset}; | |
| 66 return completion; | |
| 67 } | |
| 68 | |
| 69 } // namespace | |
| 70 | |
| 71 | 38 | 
| 72 class RegisterAllocatorTest : public TestWithZone { | 39 class RegisterAllocatorTest : public TestWithZone { | 
| 73 public: | 40 public: | 
| 74 static const int kDefaultNRegs = 4; | 41 static const int kDefaultNRegs = 4; | 
| 42 static const int kNoValue = kMinInt; | |
| 43 | |
| 44 struct VReg { | |
| 45 VReg() : value_(kNoValue) {} | |
| 46 VReg(PhiInstruction* phi) : value_(phi->virtual_register()) {} // NOLINT | |
| 47 explicit VReg(int value) : value_(value) {} | |
| 48 int value_; | |
| 49 }; | |
| 50 | |
| 51 enum TestOperandType { | |
| 52 kInvalid, | |
| 53 kSameAsFirst, | |
| 54 kRegister, | |
| 55 kFixedRegister, | |
| 56 kSlot, | |
| 57 kFixedSlot, | |
| 58 kImmediate, | |
| 59 kNone | |
| 60 }; | |
| 61 | |
| 62 struct TestOperand { | |
| 63 TestOperand() : type_(kInvalid), vreg_(), value_(kNoValue) {} | |
| 64 TestOperand(TestOperandType type, int imm) | |
| 65 : type_(type), vreg_(), value_(imm) {} | |
| 66 TestOperand(TestOperandType type, VReg vreg, int value = kNoValue) | |
| 67 : type_(type), vreg_(vreg), value_(value) {} | |
| 68 | |
| 69 TestOperandType type_; | |
| 70 VReg vreg_; | |
| 71 int value_; | |
| 72 }; | |
| 73 | |
| 74 static TestOperand Same() { return TestOperand(kSameAsFirst, VReg()); } | |
| 75 | |
| 76 static TestOperand Reg(VReg vreg, int index = kNoValue) { | |
| 77 TestOperandType type = kRegister; | |
| 78 if (index != kNoValue) type = kFixedRegister; | |
| 79 return TestOperand(type, vreg, index); | |
| 80 } | |
| 81 | |
| 82 static TestOperand Reg(int index = kNoValue) { return Reg(VReg(), index); } | |
| 83 | |
| 84 static TestOperand Slot(VReg vreg, int index = kNoValue) { | |
| 85 TestOperandType type = kSlot; | |
| 86 if (index != kNoValue) type = kFixedSlot; | |
| 87 return TestOperand(type, vreg, index); | |
| 88 } | |
| 89 | |
| 90 static TestOperand Slot(int index = kNoValue) { return Slot(VReg(), index); } | |
| 91 | |
| 92 static TestOperand Use(VReg vreg) { return TestOperand(kNone, vreg); } | |
| 93 | |
| 94 static TestOperand Use() { return Use(VReg()); } | |
| 95 | |
| 96 enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump }; | |
| 97 | |
| 98 struct BlockCompletion { | |
| 99 BlockCompletionType type_; | |
| 100 TestOperand op_; | |
| 101 int offset_0_; | |
| 102 int offset_1_; | |
| 103 }; | |
| 104 | |
| 105 static BlockCompletion FallThrough() { | |
| 106 BlockCompletion completion = {kFallThrough, TestOperand(), 1, kNoValue}; | |
| 107 return completion; | |
| 108 } | |
| 109 | |
| 110 static BlockCompletion Jump(int offset) { | |
| 111 BlockCompletion completion = {kJump, TestOperand(), offset, kNoValue}; | |
| 112 return completion; | |
| 113 } | |
| 114 | |
| 115 static BlockCompletion Branch(TestOperand op, int left_offset, | |
| 116 int right_offset) { | |
| 117 BlockCompletion completion = {kBranch, op, left_offset, right_offset}; | |
| 118 return completion; | |
| 119 } | |
| 120 | |
| 121 static BlockCompletion Last() { | |
| 122 BlockCompletion completion = {kBlockEnd, TestOperand(), kNoValue, kNoValue}; | |
| 123 return completion; | |
| 124 } | |
| 75 | 125 | 
| 76 RegisterAllocatorTest() | 126 RegisterAllocatorTest() | 
| 77 : num_general_registers_(kDefaultNRegs), | 127 : num_general_registers_(kDefaultNRegs), | 
| 78 num_double_registers_(kDefaultNRegs), | 128 num_double_registers_(kDefaultNRegs), | 
| 79 instruction_blocks_(zone()), | 129 instruction_blocks_(zone()), | 
| 130 current_instruction_index_(-1), | |
| 80 current_block_(nullptr), | 131 current_block_(nullptr), | 
| 81 is_last_block_(false), | |
| 82 block_returns_(false) { | 132 block_returns_(false) { | 
| 83 InitializeRegisterNames(); | 133 InitializeRegisterNames(); | 
| 84 } | 134 } | 
| 85 | 135 | 
| 86 void SetNumRegs(int num_general_registers, int num_double_registers) { | 136 void SetNumRegs(int num_general_registers, int num_double_registers) { | 
| 137 CHECK(config_.is_empty()); | |
| 138 CHECK(instructions_.empty()); | |
| 87 CHECK(instruction_blocks_.empty()); | 139 CHECK(instruction_blocks_.empty()); | 
| 88 num_general_registers_ = num_general_registers; | 140 num_general_registers_ = num_general_registers; | 
| 89 num_double_registers_ = num_double_registers; | 141 num_double_registers_ = num_double_registers; | 
| 90 } | 142 } | 
| 91 | 143 | 
| 92 RegisterConfiguration* config() { | 144 RegisterConfiguration* config() { | 
| 93 if (config_.is_empty()) { | 145 if (config_.is_empty()) { | 
| 94 config_.Reset(new RegisterConfiguration( | 146 config_.Reset(new RegisterConfiguration( | 
| 95 num_general_registers_, num_double_registers_, num_double_registers_, | 147 num_general_registers_, num_double_registers_, num_double_registers_, | 
| 96 general_register_names_, double_register_names_)); | 148 general_register_names_, double_register_names_)); | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 loop_blocks_.push_back(loop_data); | 181 loop_blocks_.push_back(loop_data); | 
| 130 } | 182 } | 
| 131 | 183 | 
| 132 void EndLoop() { | 184 void EndLoop() { | 
| 133 CHECK(current_block_ == nullptr); | 185 CHECK(current_block_ == nullptr); | 
| 134 CHECK(!loop_blocks_.empty()); | 186 CHECK(!loop_blocks_.empty()); | 
| 135 CHECK_EQ(0, loop_blocks_.back().expected_blocks_); | 187 CHECK_EQ(0, loop_blocks_.back().expected_blocks_); | 
| 136 loop_blocks_.pop_back(); | 188 loop_blocks_.pop_back(); | 
| 137 } | 189 } | 
| 138 | 190 | 
| 139 void StartLastBlock() { | |
| 140 CHECK(!is_last_block_); | |
| 141 is_last_block_ = true; | |
| 142 NewBlock(); | |
| 143 } | |
| 144 | |
| 145 void StartBlock() { | 191 void StartBlock() { | 
| 146 CHECK(!is_last_block_); | |
| 147 block_returns_ = false; | 192 block_returns_ = false; | 
| 148 NewBlock(); | 193 NewBlock(); | 
| 149 } | 194 } | 
| 150 | 195 | 
| 151 void EndBlock(BlockCompletion completion = FallThrough()) { | 196 int EndBlock(BlockCompletion completion = FallThrough()) { | 
| 152 completions_.push_back(completion); | 197 int instruction_index = kMinInt; | 
| 198 if (block_returns_) { | |
| 199 CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough); | |
| 200 completion.type_ = kBlockEnd; | |
| 201 } | |
| 153 switch (completion.type_) { | 202 switch (completion.type_) { | 
| 154 case kBlockEnd: | 203 case kBlockEnd: | 
| 155 CHECK(false); // unreachable; | |
| 156 break; | 204 break; | 
| 157 case kFallThrough: | 205 case kFallThrough: | 
| 158 if (is_last_block_ || block_returns_) { | 206 instruction_index = EmitFallThrough(); | 
| 159 completions_.back().type_ = kBlockEnd; | |
| 160 break; | |
| 161 } | |
| 162 EmitFallThrough(); | |
| 163 break; | 207 break; | 
| 164 case kJump: | 208 case kJump: | 
| 165 CHECK(!block_returns_); | 209 CHECK(!block_returns_); | 
| 166 EmitJump(); | 210 instruction_index = EmitJump(); | 
| 167 break; | 211 break; | 
| 168 case kBranch: | 212 case kBranch: | 
| 169 CHECK(!block_returns_); | 213 CHECK(!block_returns_); | 
| 170 EmitBranch(completion.vreg_); | 214 instruction_index = EmitBranch(completion.op_); | 
| 171 break; | 215 break; | 
| 172 } | 216 } | 
| 217 completions_.push_back(completion); | |
| 173 CHECK(current_block_ != nullptr); | 218 CHECK(current_block_ != nullptr); | 
| 174 sequence()->EndBlock(current_block_->rpo_number()); | 219 sequence()->EndBlock(current_block_->rpo_number()); | 
| 175 current_block_ = nullptr; | 220 current_block_ = nullptr; | 
| 221 return instruction_index; | |
| 176 } | 222 } | 
| 177 | 223 | 
| 178 void Allocate() { | 224 void Allocate() { | 
| 179 CHECK_EQ(nullptr, current_block_); | 225 CHECK_EQ(nullptr, current_block_); | 
| 180 CHECK(is_last_block_); | |
| 181 WireBlocks(); | 226 WireBlocks(); | 
| 182 RegisterAllocatorVerifier verifier(zone(), config(), sequence()); | 227 RegisterAllocatorVerifier verifier(zone(), config(), sequence()); | 
| 183 if (FLAG_trace_alloc || FLAG_trace_turbo) { | 228 if (FLAG_trace_alloc || FLAG_trace_turbo) { | 
| 184 OFStream os(stdout); | 229 OFStream os(stdout); | 
| 185 PrintableInstructionSequence printable = {config(), sequence()}; | 230 PrintableInstructionSequence printable = {config(), sequence()}; | 
| 186 os << "Before: " << std::endl << printable << std::endl; | 231 os << "Before: " << std::endl << printable << std::endl; | 
| 187 } | 232 } | 
| 188 allocator()->Allocate(); | 233 allocator()->Allocate(); | 
| 189 if (FLAG_trace_alloc || FLAG_trace_turbo) { | 234 if (FLAG_trace_alloc || FLAG_trace_turbo) { | 
| 190 OFStream os(stdout); | 235 OFStream os(stdout); | 
| 191 PrintableInstructionSequence printable = {config(), sequence()}; | 236 PrintableInstructionSequence printable = {config(), sequence()}; | 
| 192 os << "After: " << std::endl << printable << std::endl; | 237 os << "After: " << std::endl << printable << std::endl; | 
| 193 } | 238 } | 
| 194 verifier.VerifyAssignment(); | 239 verifier.VerifyAssignment(); | 
| 195 verifier.VerifyGapMoves(); | 240 verifier.VerifyGapMoves(); | 
| 196 } | 241 } | 
| 197 | 242 | 
| 198 int NewReg() { return sequence()->NextVirtualRegister(); } | 243 TestOperand Imm(int32_t imm = 0) { | 
| 244 int index = sequence()->AddImmediate(Constant(imm)); | |
| 245 return TestOperand(kImmediate, index); | |
| 246 } | |
| 199 | 247 | 
| 200 int Parameter() { | 248 VReg Parameter(TestOperand output_op = Reg()) { | 
| 201 int vreg = NewReg(); | 249 VReg vreg = NewReg(); | 
| 202 InstructionOperand* outputs[1]{UseRegister(vreg)}; | 250 InstructionOperand* outputs[1]{ConvertOutputOp(vreg, output_op)}; | 
| 203 Emit(kArchNop, 1, outputs); | 251 Emit(vreg.value_, kArchNop, 1, outputs); | 
| 204 return vreg; | 252 return vreg; | 
| 205 } | 253 } | 
| 206 | 254 | 
| 207 Instruction* Return(int vreg) { | 255 int Return(TestOperand input_op_0) { | 
| 208 block_returns_ = true; | 256 block_returns_ = true; | 
| 209 InstructionOperand* inputs[1]{UseRegister(vreg)}; | 257 InstructionOperand* inputs[1]{ConvertInputOp(input_op_0)}; | 
| 210 return Emit(kArchRet, 0, nullptr, 1, inputs); | 258 return Emit(NewIndex(), kArchRet, 0, nullptr, 1, inputs); | 
| 211 } | 259 } | 
| 212 | 260 | 
| 213 PhiInstruction* Phi(int vreg) { | 261 int Return(VReg vreg) { return Return(Reg(vreg, 0)); } | 
| 214 PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg()); | 262 | 
| 215 phi->operands().push_back(vreg); | 263 PhiInstruction* Phi(VReg incoming_vreg) { | 
| 264 PhiInstruction* phi = new (zone()) PhiInstruction(zone(), NewReg().value_); | |
| 265 phi->operands().push_back(incoming_vreg.value_); | |
| 216 current_block_->AddPhi(phi); | 266 current_block_->AddPhi(phi); | 
| 217 return phi; | 267 return phi; | 
| 218 } | 268 } | 
| 219 | 269 | 
| 220 int DefineConstant(int32_t imm = 0) { | 270 PhiInstruction* Phi(VReg incoming_vreg_0, VReg incoming_vreg_1) { | 
| 221 int virtual_register = NewReg(); | 271 auto phi = Phi(incoming_vreg_0); | 
| 222 sequence()->AddConstant(virtual_register, Constant(imm)); | 272 Extend(phi, incoming_vreg_1); | 
| 223 InstructionOperand* outputs[1]{ | 273 return phi; | 
| 224 ConstantOperand::Create(virtual_register, zone())}; | |
| 225 Emit(kArchNop, 1, outputs); | |
| 226 return virtual_register; | |
| 227 } | 274 } | 
| 228 | 275 | 
| 229 ImmediateOperand* Immediate(int32_t imm = 0) { | 276 static void Extend(PhiInstruction* phi, VReg vreg) { | 
| 230 int index = sequence()->AddImmediate(Constant(imm)); | 277 phi->operands().push_back(vreg.value_); | 
| 231 return ImmediateOperand::Create(index, zone()); | |
| 232 } | 278 } | 
| 233 | 279 | 
| 234 Instruction* EmitFRI(int output_vreg, int input_vreg_0) { | 280 VReg DefineConstant(int32_t imm = 0) { | 
| 235 InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)}; | 281 VReg vreg = NewReg(); | 
| 236 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Immediate()}; | 282 sequence()->AddConstant(vreg.value_, Constant(imm)); | 
| 237 return Emit(kArchNop, 1, outputs, 2, inputs); | 283 InstructionOperand* outputs[1]{ | 
| 284 ConstantOperand::Create(vreg.value_, zone())}; | |
| 285 Emit(vreg.value_, kArchNop, 1, outputs); | |
| 286 return vreg; | |
| 238 } | 287 } | 
| 239 | 288 | 
| 240 Instruction* EmitFRU(int output_vreg, int input_vreg_0, int input_vreg_1) { | 289 VReg EmitOII(TestOperand output_op, TestOperand input_op_0, | 
| 241 InstructionOperand* outputs[1]{DefineSameAsFirst(output_vreg)}; | 290 TestOperand input_op_1) { | 
| 242 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), Use(input_vreg_1)}; | 291 VReg output_vreg = NewReg(); | 
| 243 return Emit(kArchNop, 1, outputs, 2, inputs); | 292 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; | 
| 293 InstructionOperand* inputs[2]{ConvertInputOp(input_op_0), | |
| 294 ConvertInputOp(input_op_1)}; | |
| 295 Emit(output_vreg.value_, kArchNop, 1, outputs, 2, inputs); | |
| 296 return output_vreg; | |
| 244 } | 297 } | 
| 245 | 298 | 
| 246 Instruction* EmitRRR(int output_vreg, int input_vreg_0, int input_vreg_1) { | 299 VReg EmitCall(TestOperand output_op, size_t input_size, TestOperand* inputs) { | 
| 247 InstructionOperand* outputs[1]{UseRegister(output_vreg)}; | 300 VReg output_vreg = NewReg(); | 
| 248 InstructionOperand* inputs[2]{UseRegister(input_vreg_0), | 301 InstructionOperand* outputs[1]{ConvertOutputOp(output_vreg, output_op)}; | 
| 249 UseRegister(input_vreg_1)}; | 302 InstructionOperand** mapped_inputs = | 
| 250 return Emit(kArchNop, 1, outputs, 2, inputs); | 303 zone()->NewArray<InstructionOperand*>(static_cast<int>(input_size)); | 
| 304 for (size_t i = 0; i < input_size; ++i) { | |
| 305 mapped_inputs[i] = ConvertInputOp(inputs[i]); | |
| 306 } | |
| 307 Emit(output_vreg.value_, kArchCallCodeObject, 1, outputs, input_size, | |
| 308 mapped_inputs); | |
| 309 return output_vreg; | |
| 310 } | |
| 311 | |
| 312 // Get defining instruction vreg or value returned at instruction creation | |
| 313 // time when there is no return value. | |
| 314 const Instruction* GetInstruction(int instruction_index) { | |
| 315 auto it = instructions_.find(instruction_index); | |
| 316 CHECK(it != instructions_.end()); | |
| 317 return it->second; | |
| 251 } | 318 } | 
| 252 | 319 | 
| 253 private: | 320 private: | 
| 254 InstructionOperand* Unallocated(int vreg, | 321 VReg NewReg() { return VReg(sequence()->NextVirtualRegister()); } | 
| 255 UnallocatedOperand::ExtendedPolicy policy) { | 322 int NewIndex() { return current_instruction_index_--; } | 
| 256 UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy); | 323 | 
| 257 op->set_virtual_register(vreg); | 324 static TestOperand Invalid() { return TestOperand(kInvalid, VReg()); } | 
| 258 return op; | 325 | 
| 326 int EmitBranch(TestOperand input_op) { | |
| 327 InstructionOperand* inputs[4]{ConvertInputOp(input_op), | |
| 328 ConvertInputOp(Imm()), ConvertInputOp(Imm()), | |
| 329 ConvertInputOp(Imm())}; | |
| 330 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) | | |
| 331 FlagsConditionField::encode(kEqual); | |
| 332 auto* instruction = | |
| 333 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl(); | |
| 334 return AddInstruction(NewIndex(), instruction); | |
| 259 } | 335 } | 
| 260 | 336 | 
| 261 InstructionOperand* Unallocated(int vreg, | 337 int EmitFallThrough() { | 
| 262 UnallocatedOperand::ExtendedPolicy policy, | 338 auto* instruction = NewInstruction(kArchNop, 0, nullptr)->MarkAsControl(); | 
| 263 UnallocatedOperand::Lifetime lifetime) { | 339 return AddInstruction(NewIndex(), instruction); | 
| 264 UnallocatedOperand* op = new (zone()) UnallocatedOperand(policy, lifetime); | |
| 265 op->set_virtual_register(vreg); | |
| 266 return op; | |
| 267 } | 340 } | 
| 268 | 341 | 
| 269 InstructionOperand* UseRegister(int vreg) { | 342 int EmitJump() { | 
| 270 return Unallocated(vreg, UnallocatedOperand::MUST_HAVE_REGISTER); | 343 InstructionOperand* inputs[1]{ConvertInputOp(Imm())}; | 
| 271 } | 344 auto* instruction = | 
| 272 | |
| 273 InstructionOperand* DefineSameAsFirst(int vreg) { | |
| 274 return Unallocated(vreg, UnallocatedOperand::SAME_AS_FIRST_INPUT); | |
| 275 } | |
| 276 | |
| 277 InstructionOperand* Use(int vreg) { | |
| 278 return Unallocated(vreg, UnallocatedOperand::NONE, | |
| 279 UnallocatedOperand::USED_AT_START); | |
| 280 } | |
| 281 | |
| 282 void EmitBranch(int vreg) { | |
| 283 InstructionOperand* inputs[4]{UseRegister(vreg), Immediate(), Immediate(), | |
| 284 Immediate()}; | |
| 285 InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) | | |
| 286 FlagsConditionField::encode(kEqual); | |
| 287 Instruction* instruction = | |
| 288 NewInstruction(opcode, 0, nullptr, 4, inputs)->MarkAsControl(); | |
| 289 sequence()->AddInstruction(instruction); | |
| 290 } | |
| 291 | |
| 292 void EmitFallThrough() { | |
| 293 Instruction* instruction = | |
| 294 NewInstruction(kArchNop, 0, nullptr)->MarkAsControl(); | |
| 295 sequence()->AddInstruction(instruction); | |
| 296 } | |
| 297 | |
| 298 void EmitJump() { | |
| 299 InstructionOperand* inputs[1]{Immediate()}; | |
| 300 Instruction* instruction = | |
| 301 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl(); | 345 NewInstruction(kArchJmp, 0, nullptr, 1, inputs)->MarkAsControl(); | 
| 302 sequence()->AddInstruction(instruction); | 346 return AddInstruction(NewIndex(), instruction); | 
| 303 } | 347 } | 
| 304 | 348 | 
| 305 Instruction* NewInstruction(InstructionCode code, size_t outputs_size, | 349 Instruction* NewInstruction(InstructionCode code, size_t outputs_size, | 
| 306 InstructionOperand** outputs, | 350 InstructionOperand** outputs, | 
| 307 size_t inputs_size = 0, | 351 size_t inputs_size = 0, | 
| 308 InstructionOperand* *inputs = nullptr, | 352 InstructionOperand* *inputs = nullptr, | 
| 309 size_t temps_size = 0, | 353 size_t temps_size = 0, | 
| 310 InstructionOperand* *temps = nullptr) { | 354 InstructionOperand* *temps = nullptr) { | 
| 311 CHECK_NE(nullptr, current_block_); | 355 CHECK_NE(nullptr, current_block_); | 
| 312 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size, | 356 return Instruction::New(zone(), code, outputs_size, outputs, inputs_size, | 
| 313 inputs, temps_size, temps); | 357 inputs, temps_size, temps); | 
| 314 } | 358 } | 
| 315 | 359 | 
| 316 Instruction* Emit(InstructionCode code, size_t outputs_size, | 360 InstructionOperand* Unallocated(TestOperand op, | 
| 317 InstructionOperand** outputs, size_t inputs_size = 0, | 361 UnallocatedOperand::ExtendedPolicy policy) { | 
| 318 InstructionOperand* *inputs = nullptr, | 362 auto* unallocated = new (zone()) UnallocatedOperand(policy); | 
| 
Benedikt Meurer
2014/11/13 09:22:04
auto* looks weird, how about auto instead? :-)
 | |
| 319 size_t temps_size = 0, | 363 unallocated->set_virtual_register(op.vreg_.value_); | 
| 320 InstructionOperand* *temps = nullptr) { | 364 return unallocated; | 
| 321 Instruction* instruction = NewInstruction( | 365 } | 
| 322 code, outputs_size, outputs, inputs_size, inputs, temps_size, temps); | 366 | 
| 323 sequence()->AddInstruction(instruction); | 367 InstructionOperand* Unallocated(TestOperand op, | 
| 324 return instruction; | 368 UnallocatedOperand::ExtendedPolicy policy, | 
| 369 UnallocatedOperand::Lifetime lifetime) { | |
| 370 auto* unallocated = new (zone()) UnallocatedOperand(policy, lifetime); | |
| 371 unallocated->set_virtual_register(op.vreg_.value_); | |
| 372 return unallocated; | |
| 373 } | |
| 374 | |
| 375 InstructionOperand* Unallocated(TestOperand op, | |
| 376 UnallocatedOperand::ExtendedPolicy policy, | |
| 377 int index) { | |
| 378 auto* unallocated = new (zone()) UnallocatedOperand(policy, index); | |
| 379 unallocated->set_virtual_register(op.vreg_.value_); | |
| 380 return unallocated; | |
| 381 } | |
| 382 | |
| 383 InstructionOperand* Unallocated(TestOperand op, | |
| 384 UnallocatedOperand::BasicPolicy policy, | |
| 385 int index) { | |
| 386 auto* unallocated = new (zone()) UnallocatedOperand(policy, index); | |
| 387 unallocated->set_virtual_register(op.vreg_.value_); | |
| 388 return unallocated; | |
| 389 } | |
| 390 | |
| 391 InstructionOperand* ConvertInputOp(TestOperand op) { | |
| 392 if (op.type_ == kImmediate) { | |
| 393 CHECK_EQ(op.vreg_.value_, kNoValue); | |
| 394 return ImmediateOperand::Create(op.value_, zone()); | |
| 395 } | |
| 396 CHECK_NE(op.vreg_.value_, kNoValue); | |
| 397 switch (op.type_) { | |
| 398 case kNone: | |
| 399 return Unallocated(op, UnallocatedOperand::NONE, | |
| 400 UnallocatedOperand::USED_AT_START); | |
| 401 case kRegister: | |
| 402 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER, | |
| 403 UnallocatedOperand::USED_AT_START); | |
| 404 case kFixedRegister: | |
| 405 CHECK(0 <= op.value_ && op.value_ < num_general_registers_); | |
| 406 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_); | |
| 407 default: | |
| 408 break; | |
| 409 } | |
| 410 CHECK(false); | |
| 411 return NULL; | |
| 412 } | |
| 413 | |
| 414 InstructionOperand* ConvertOutputOp(VReg vreg, TestOperand op) { | |
| 415 CHECK_EQ(op.vreg_.value_, kNoValue); | |
| 416 op.vreg_ = vreg; | |
| 417 switch (op.type_) { | |
| 418 case kSameAsFirst: | |
| 419 return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT); | |
| 420 case kRegister: | |
| 421 return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER); | |
| 422 case kFixedSlot: | |
| 423 return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_); | |
| 424 case kFixedRegister: | |
| 425 CHECK(0 <= op.value_ && op.value_ < num_general_registers_); | |
| 426 return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_); | |
| 427 default: | |
| 428 break; | |
| 429 } | |
| 430 CHECK(false); | |
| 431 return NULL; | |
| 325 } | 432 } | 
| 326 | 433 | 
| 327 InstructionBlock* NewBlock() { | 434 InstructionBlock* NewBlock() { | 
| 328 CHECK(current_block_ == nullptr); | 435 CHECK(current_block_ == nullptr); | 
| 329 BasicBlock::Id block_id = | 436 auto block_id = BasicBlock::Id::FromSize(instruction_blocks_.size()); | 
| 330 BasicBlock::Id::FromSize(instruction_blocks_.size()); | |
| 331 Rpo rpo = Rpo::FromInt(block_id.ToInt()); | 437 Rpo rpo = Rpo::FromInt(block_id.ToInt()); | 
| 332 Rpo loop_header = Rpo::Invalid(); | 438 Rpo loop_header = Rpo::Invalid(); | 
| 333 Rpo loop_end = Rpo::Invalid(); | 439 Rpo loop_end = Rpo::Invalid(); | 
| 334 if (!loop_blocks_.empty()) { | 440 if (!loop_blocks_.empty()) { | 
| 335 auto& loop_data = loop_blocks_.back(); | 441 auto& loop_data = loop_blocks_.back(); | 
| 336 // This is a loop header. | 442 // This is a loop header. | 
| 337 if (!loop_data.loop_header_.IsValid()) { | 443 if (!loop_data.loop_header_.IsValid()) { | 
| 338 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_); | 444 loop_end = Rpo::FromInt(block_id.ToInt() + loop_data.expected_blocks_); | 
| 339 loop_data.expected_blocks_--; | 445 loop_data.expected_blocks_--; | 
| 340 loop_data.loop_header_ = rpo; | 446 loop_data.loop_header_ = rpo; | 
| 341 } else { | 447 } else { | 
| 342 // This is a loop body. | 448 // This is a loop body. | 
| 343 CHECK_NE(0, loop_data.expected_blocks_); | 449 CHECK_NE(0, loop_data.expected_blocks_); | 
| 344 // TODO(dcarney): handle nested loops. | 450 // TODO(dcarney): handle nested loops. | 
| 345 loop_data.expected_blocks_--; | 451 loop_data.expected_blocks_--; | 
| 346 loop_header = loop_data.loop_header_; | 452 loop_header = loop_data.loop_header_; | 
| 347 } | 453 } | 
| 348 } | 454 } | 
| 349 // Construct instruction block. | 455 // Construct instruction block. | 
| 350 InstructionBlock* instruction_block = new (zone()) InstructionBlock( | 456 auto* instruction_block = new (zone()) InstructionBlock( | 
| 351 zone(), block_id, rpo, rpo, loop_header, loop_end, false); | 457 zone(), block_id, rpo, rpo, loop_header, loop_end, false); | 
| 352 instruction_blocks_.push_back(instruction_block); | 458 instruction_blocks_.push_back(instruction_block); | 
| 353 current_block_ = instruction_block; | 459 current_block_ = instruction_block; | 
| 354 sequence()->StartBlock(rpo); | 460 sequence()->StartBlock(rpo); | 
| 355 return instruction_block; | 461 return instruction_block; | 
| 356 } | 462 } | 
| 357 | 463 | 
| 358 void WireBlocks() { | 464 void WireBlocks() { | 
| 359 CHECK(instruction_blocks_.size() == completions_.size()); | 465 CHECK(instruction_blocks_.size() == completions_.size()); | 
| 360 size_t offset = 0; | 466 size_t offset = 0; | 
| (...skipping 12 matching lines...) Expand all Loading... | |
| 373 } | 479 } | 
| 374 ++offset; | 480 ++offset; | 
| 375 } | 481 } | 
| 376 } | 482 } | 
| 377 | 483 | 
| 378 void WireBlock(size_t block_offset, int jump_offset) { | 484 void WireBlock(size_t block_offset, int jump_offset) { | 
| 379 size_t target_block_offset = | 485 size_t target_block_offset = | 
| 380 block_offset + static_cast<size_t>(jump_offset); | 486 block_offset + static_cast<size_t>(jump_offset); | 
| 381 CHECK(block_offset < instruction_blocks_.size()); | 487 CHECK(block_offset < instruction_blocks_.size()); | 
| 382 CHECK(target_block_offset < instruction_blocks_.size()); | 488 CHECK(target_block_offset < instruction_blocks_.size()); | 
| 383 InstructionBlock* block = instruction_blocks_[block_offset]; | 489 auto* block = instruction_blocks_[block_offset]; | 
| 384 InstructionBlock* target = instruction_blocks_[target_block_offset]; | 490 auto* target = instruction_blocks_[target_block_offset]; | 
| 385 block->successors().push_back(target->rpo_number()); | 491 block->successors().push_back(target->rpo_number()); | 
| 386 target->predecessors().push_back(block->rpo_number()); | 492 target->predecessors().push_back(block->rpo_number()); | 
| 387 } | 493 } | 
| 388 | 494 | 
| 495 int Emit(int instruction_index, InstructionCode code, size_t outputs_size, | |
| 496 InstructionOperand** outputs, size_t inputs_size = 0, | |
| 497 InstructionOperand* *inputs = nullptr, size_t temps_size = 0, | |
| 498 InstructionOperand* *temps = nullptr) { | |
| 499 auto* instruction = NewInstruction(code, outputs_size, outputs, inputs_size, | |
| 500 inputs, temps_size, temps); | |
| 501 return AddInstruction(instruction_index, instruction); | |
| 502 } | |
| 503 | |
| 504 int AddInstruction(int instruction_index, Instruction* instruction) { | |
| 505 sequence()->AddInstruction(instruction); | |
| 506 return instruction_index; | |
| 507 } | |
| 508 | |
| 389 struct LoopData { | 509 struct LoopData { | 
| 390 Rpo loop_header_; | 510 Rpo loop_header_; | 
| 391 int expected_blocks_; | 511 int expected_blocks_; | 
| 392 }; | 512 }; | 
| 513 | |
| 393 typedef std::vector<LoopData> LoopBlocks; | 514 typedef std::vector<LoopData> LoopBlocks; | 
| 515 typedef std::map<int, const Instruction*> Instructions; | |
| 394 typedef std::vector<BlockCompletion> Completions; | 516 typedef std::vector<BlockCompletion> Completions; | 
| 395 | 517 | 
| 396 SmartPointer<RegisterConfiguration> config_; | 518 SmartPointer<RegisterConfiguration> config_; | 
| 397 SmartPointer<Frame> frame_; | 519 SmartPointer<Frame> frame_; | 
| 398 SmartPointer<RegisterAllocator> allocator_; | 520 SmartPointer<RegisterAllocator> allocator_; | 
| 399 SmartPointer<InstructionSequence> sequence_; | 521 SmartPointer<InstructionSequence> sequence_; | 
| 400 int num_general_registers_; | 522 int num_general_registers_; | 
| 401 int num_double_registers_; | 523 int num_double_registers_; | 
| 402 | 524 | 
| 403 // Block building state. | 525 // Block building state. | 
| 404 InstructionBlocks instruction_blocks_; | 526 InstructionBlocks instruction_blocks_; | 
| 527 Instructions instructions_; | |
| 528 int current_instruction_index_; | |
| 405 Completions completions_; | 529 Completions completions_; | 
| 406 LoopBlocks loop_blocks_; | 530 LoopBlocks loop_blocks_; | 
| 407 InstructionBlock* current_block_; | 531 InstructionBlock* current_block_; | 
| 408 bool is_last_block_; | |
| 409 bool block_returns_; | 532 bool block_returns_; | 
| 410 }; | 533 }; | 
| 411 | 534 | 
| 412 | 535 | 
| 413 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { | 536 TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) { | 
| 414 StartLastBlock(); | 537 // return p0 + p1; | 
| 415 int a_reg = Parameter(); | 538 StartBlock(); | 
| 416 int b_reg = Parameter(); | 539 auto a_reg = Parameter(); | 
| 417 int c_reg = NewReg(); | 540 auto b_reg = Parameter(); | 
| 418 Instruction* res = EmitRRR(c_reg, a_reg, b_reg); | 541 auto c_reg = EmitOII(Reg(1), Reg(a_reg, 1), Reg(b_reg, 0)); | 
| 419 Return(c_reg); | 542 Return(c_reg); | 
| 420 EndBlock(); | 543 EndBlock(Last()); | 
| 421 | 544 | 
| 422 Allocate(); | 545 Allocate(); | 
| 423 | |
| 424 ASSERT_TRUE(res->OutputAt(0)->IsRegister()); | |
| 425 } | 546 } | 
| 426 | 547 | 
| 427 | 548 | 
| 428 TEST_F(RegisterAllocatorTest, SimpleLoop) { | 549 TEST_F(RegisterAllocatorTest, SimpleLoop) { | 
| 429 // i = K; | 550 // i = K; | 
| 430 // while(true) { i++ } | 551 // while(true) { i++ } | 
| 431 | |
| 432 StartBlock(); | 552 StartBlock(); | 
| 433 int i_reg = DefineConstant(); | 553 auto i_reg = DefineConstant(); | 
| 434 EndBlock(); | 554 EndBlock(); | 
| 435 | 555 | 
| 436 { | 556 { | 
| 437 StartLoop(1); | 557 StartLoop(1); | 
| 438 | 558 | 
| 439 StartLastBlock(); | 559 StartBlock(); | 
| 440 PhiInstruction* phi = Phi(i_reg); | 560 auto* phi = Phi(i_reg); | 
| 441 int ipp = NewReg(); | 561 auto ipp = EmitOII(Same(), Reg(phi), Use(DefineConstant())); | 
| 442 EmitFRU(ipp, phi->virtual_register(), DefineConstant()); | 562 Extend(phi, ipp); | 
| 443 phi->operands().push_back(ipp); | |
| 444 EndBlock(Jump(0)); | 563 EndBlock(Jump(0)); | 
| 445 | 564 | 
| 446 EndLoop(); | 565 EndLoop(); | 
| 447 } | 566 } | 
| 448 | 567 | 
| 449 Allocate(); | 568 Allocate(); | 
| 450 } | 569 } | 
| 451 | 570 | 
| 452 | 571 | 
| 453 TEST_F(RegisterAllocatorTest, SimpleBranch) { | 572 TEST_F(RegisterAllocatorTest, SimpleBranch) { | 
| 454 // return i ? K1 : K2 | 573 // return i ? K1 : K2 | 
| 455 StartBlock(); | 574 StartBlock(); | 
| 456 int i_reg = DefineConstant(); | 575 auto i = DefineConstant(); | 
| 457 EndBlock(Branch(i_reg, 1, 2)); | 576 EndBlock(Branch(Reg(i), 1, 2)); | 
| 458 | 577 | 
| 459 StartBlock(); | 578 StartBlock(); | 
| 460 Return(DefineConstant()); | 579 Return(DefineConstant()); | 
| 461 EndBlock(); | 580 EndBlock(Last()); | 
| 462 | 581 | 
| 463 StartLastBlock(); | 582 StartBlock(); | 
| 464 Return(DefineConstant()); | 583 Return(DefineConstant()); | 
| 584 EndBlock(Last()); | |
| 585 | |
| 586 Allocate(); | |
| 587 } | |
| 588 | |
| 589 | |
| 590 TEST_F(RegisterAllocatorTest, SimpleDiamond) { | |
| 591 // return p0 ? p0 : p0 | |
| 592 StartBlock(); | |
| 593 auto param = Parameter(); | |
| 594 EndBlock(Branch(Reg(param), 1, 2)); | |
| 595 | |
| 596 StartBlock(); | |
| 597 EndBlock(Jump(2)); | |
| 598 | |
| 599 StartBlock(); | |
| 600 EndBlock(Jump(1)); | |
| 601 | |
| 602 StartBlock(); | |
| 603 Return(param); | |
| 465 EndBlock(); | 604 EndBlock(); | 
| 466 | 605 | 
| 467 Allocate(); | 606 Allocate(); | 
| 607 } | |
| 608 | |
| 609 | |
| 610 TEST_F(RegisterAllocatorTest, SimpleDiamondPhi) { | |
| 611 // return i ? K1 : K2 | |
| 612 StartBlock(); | |
| 613 EndBlock(Branch(Reg(DefineConstant()), 1, 2)); | |
| 614 | |
| 615 StartBlock(); | |
| 616 auto t_val = DefineConstant(); | |
| 617 EndBlock(Jump(2)); | |
| 618 | |
| 619 StartBlock(); | |
| 620 auto f_val = DefineConstant(); | |
| 621 EndBlock(Jump(1)); | |
| 622 | |
| 623 StartBlock(); | |
| 624 Return(Reg(Phi(t_val, f_val))); | |
| 625 EndBlock(); | |
| 626 | |
| 627 Allocate(); | |
| 628 } | |
| 629 | |
| 630 | |
| 631 TEST_F(RegisterAllocatorTest, DiamondManyPhis) { | |
| 632 const int kPhis = kDefaultNRegs * 2; | |
| 633 | |
| 634 StartBlock(); | |
| 635 EndBlock(Branch(Reg(DefineConstant()), 1, 2)); | |
| 636 | |
| 637 StartBlock(); | |
| 638 VReg t_vals[kPhis]; | |
| 639 for (int i = 0; i < kPhis; ++i) { | |
| 640 t_vals[i] = DefineConstant(); | |
| 641 } | |
| 642 EndBlock(Jump(2)); | |
| 643 | |
| 644 StartBlock(); | |
| 645 VReg f_vals[kPhis]; | |
| 646 for (int i = 0; i < kPhis; ++i) { | |
| 647 f_vals[i] = DefineConstant(); | |
| 648 } | |
| 649 EndBlock(Jump(1)); | |
| 650 | |
| 651 StartBlock(); | |
| 652 TestOperand merged[kPhis]; | |
| 653 for (int i = 0; i < kPhis; ++i) { | |
| 654 merged[i] = Use(Phi(t_vals[i], f_vals[i])); | |
| 655 } | |
| 656 Return(EmitCall(Reg(), kPhis, merged)); | |
| 657 EndBlock(); | |
| 658 | |
| 659 Allocate(); | |
| 660 } | |
| 661 | |
| 662 | |
| 663 TEST_F(RegisterAllocatorTest, DoubleDiamondManyRedundantPhis) { | |
| 664 const int kPhis = kDefaultNRegs * 2; | |
| 665 | |
| 666 // First diamond. | |
| 667 StartBlock(); | |
| 668 VReg vals[kPhis]; | |
| 669 for (int i = 0; i < kPhis; ++i) { | |
| 670 vals[i] = Parameter(Slot(i)); | |
| 671 } | |
| 672 EndBlock(Branch(Reg(DefineConstant()), 1, 2)); | |
| 673 | |
| 674 StartBlock(); | |
| 675 EndBlock(Jump(2)); | |
| 676 | |
| 677 StartBlock(); | |
| 678 EndBlock(Jump(1)); | |
| 679 | |
| 680 // Second diamond. | |
| 681 StartBlock(); | |
| 682 EndBlock(Branch(Reg(DefineConstant()), 1, 2)); | |
| 683 | |
| 684 StartBlock(); | |
| 685 EndBlock(Jump(2)); | |
| 686 | |
| 687 StartBlock(); | |
| 688 EndBlock(Jump(1)); | |
| 689 | |
| 690 StartBlock(); | |
| 691 TestOperand merged[kPhis]; | |
| 692 for (int i = 0; i < kPhis; ++i) { | |
| 693 merged[i] = Use(Phi(vals[i], vals[i])); | |
| 694 } | |
| 695 Return(EmitCall(Reg(), kPhis, merged)); | |
| 696 EndBlock(); | |
| 697 | |
| 698 Allocate(); | |
| 468 } | 699 } | 
| 469 | 700 | 
| 470 | 701 | 
| 471 TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) { | 702 TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) { | 
| 472 const size_t kNumRegs = 3; | 703 const size_t kNumRegs = 3; | 
| 473 const size_t kParams = kNumRegs + 1; | 704 const size_t kParams = kNumRegs + 1; | 
| 474 int parameters[kParams]; | |
| 475 | |
| 476 // Override number of registers. | 705 // Override number of registers. | 
| 477 SetNumRegs(kNumRegs, kNumRegs); | 706 SetNumRegs(kNumRegs, kNumRegs); | 
| 478 | 707 | 
| 479 // Initial block. | |
| 480 StartBlock(); | 708 StartBlock(); | 
| 481 int constant = DefineConstant(); | 709 auto constant = DefineConstant(); | 
| 710 VReg parameters[kParams]; | |
| 482 for (size_t i = 0; i < arraysize(parameters); ++i) { | 711 for (size_t i = 0; i < arraysize(parameters); ++i) { | 
| 483 parameters[i] = DefineConstant(); | 712 parameters[i] = DefineConstant(); | 
| 484 } | 713 } | 
| 485 EndBlock(); | 714 EndBlock(); | 
| 486 | 715 | 
| 487 PhiInstruction* phis[kParams]; | 716 PhiInstruction* phis[kParams]; | 
| 488 { | 717 { | 
| 489 StartLoop(2); | 718 StartLoop(2); | 
| 490 | 719 | 
| 491 // Loop header. | 720 // Loop header. | 
| 492 StartBlock(); | 721 StartBlock(); | 
| 493 | 722 | 
| 494 for (size_t i = 0; i < arraysize(parameters); ++i) { | 723 for (size_t i = 0; i < arraysize(parameters); ++i) { | 
| 495 phis[i] = Phi(parameters[i]); | 724 phis[i] = Phi(parameters[i]); | 
| 496 } | 725 } | 
| 497 | 726 | 
| 498 // Perform some computations. | 727 // Perform some computations. | 
| 499 // something like phi[i] += const | 728 // something like phi[i] += const | 
| 500 for (size_t i = 0; i < arraysize(parameters); ++i) { | 729 for (size_t i = 0; i < arraysize(parameters); ++i) { | 
| 501 int result = NewReg(); | 730 auto result = EmitOII(Same(), Reg(phis[i]), Use(constant)); | 
| 502 EmitFRU(result, phis[i]->virtual_register(), constant); | 731 Extend(phis[i], result); | 
| 503 phis[i]->operands().push_back(result); | |
| 504 } | 732 } | 
| 505 | 733 | 
| 506 EndBlock(Branch(DefineConstant(), 1, 2)); | 734 EndBlock(Branch(Reg(DefineConstant()), 1, 2)); | 
| 507 | 735 | 
| 508 // Jump back to loop header. | 736 // Jump back to loop header. | 
| 509 StartBlock(); | 737 StartBlock(); | 
| 510 EndBlock(Jump(-1)); | 738 EndBlock(Jump(-1)); | 
| 511 | 739 | 
| 512 EndLoop(); | 740 EndLoop(); | 
| 513 } | 741 } | 
| 514 | 742 | 
| 515 // End block. | 743 StartBlock(); | 
| 516 StartLastBlock(); | |
| 517 | |
| 518 // Return sum. | |
| 519 Return(DefineConstant()); | 744 Return(DefineConstant()); | 
| 520 EndBlock(); | 745 EndBlock(); | 
| 521 | 746 | 
| 522 Allocate(); | 747 Allocate(); | 
| 523 } | 748 } | 
| 524 | 749 | 
| 525 } // namespace compiler | 750 } // namespace compiler | 
| 526 } // namespace internal | 751 } // namespace internal | 
| 527 } // namespace v8 | 752 } // namespace v8 | 
| OLD | NEW |