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); |
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 |