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 |