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/compiler/instruction.h" | 5 #include "src/compiler/instruction.h" |
6 #include "src/compiler/register-allocator-verifier.h" | 6 #include "src/compiler/register-allocator-verifier.h" |
7 | 7 |
8 namespace v8 { | 8 namespace v8 { |
9 namespace internal { | 9 namespace internal { |
10 namespace compiler { | 10 namespace compiler { |
11 | 11 |
12 static size_t OperandCount(const Instruction* instr) { | 12 static size_t OperandCount(const Instruction* instr) { |
13 return instr->InputCount() + instr->OutputCount() + instr->TempCount(); | 13 return instr->InputCount() + instr->OutputCount() + instr->TempCount(); |
14 } | 14 } |
15 | 15 |
16 | 16 |
17 static void VerifyGapEmpty(const GapInstruction* gap) { | |
18 for (int i = GapInstruction::FIRST_INNER_POSITION; | |
19 i <= GapInstruction::LAST_INNER_POSITION; i++) { | |
20 GapInstruction::InnerPosition inner_pos = | |
21 static_cast<GapInstruction::InnerPosition>(i); | |
22 CHECK_EQ(NULL, gap->GetParallelMove(inner_pos)); | |
23 } | |
24 } | |
25 | |
26 | |
27 void RegisterAllocatorVerifier::VerifyInput( | |
28 const OperandConstraint& constraint) { | |
29 CHECK_NE(kSameAsFirst, constraint.type_); | |
30 if (constraint.type_ != kImmediate) { | |
31 CHECK_NE(UnallocatedOperand::kInvalidVirtualRegister, | |
32 constraint.virtual_register_); | |
33 } | |
34 } | |
35 | |
36 | |
37 void RegisterAllocatorVerifier::VerifyTemp( | |
38 const OperandConstraint& constraint) { | |
39 CHECK_NE(kSameAsFirst, constraint.type_); | |
40 CHECK_NE(kImmediate, constraint.type_); | |
41 CHECK_NE(kConstant, constraint.type_); | |
42 CHECK_EQ(UnallocatedOperand::kInvalidVirtualRegister, | |
43 constraint.virtual_register_); | |
44 } | |
45 | |
46 | |
47 void RegisterAllocatorVerifier::VerifyOutput( | |
48 const OperandConstraint& constraint) { | |
49 CHECK_NE(kImmediate, constraint.type_); | |
50 CHECK_NE(UnallocatedOperand::kInvalidVirtualRegister, | |
51 constraint.virtual_register_); | |
52 } | |
53 | |
54 | |
17 RegisterAllocatorVerifier::RegisterAllocatorVerifier( | 55 RegisterAllocatorVerifier::RegisterAllocatorVerifier( |
18 Zone* zone, const InstructionSequence* sequence) | 56 Zone* zone, const RegisterConfiguration* config, |
19 : sequence_(sequence), constraints_(zone) { | 57 const InstructionSequence* sequence) |
58 : zone_(zone), config_(config), sequence_(sequence), constraints_(zone) { | |
20 constraints_.reserve(sequence->instructions().size()); | 59 constraints_.reserve(sequence->instructions().size()); |
60 // TODO(dcarney): model unique constraints. | |
61 // Construct OperandConstraints for all InstructionOperands, eliminating | |
62 // kSameAsFirst along the way. | |
21 for (const auto* instr : sequence->instructions()) { | 63 for (const auto* instr : sequence->instructions()) { |
22 const size_t operand_count = OperandCount(instr); | 64 const size_t operand_count = OperandCount(instr); |
23 auto* op_constraints = | 65 auto* op_constraints = |
24 zone->NewArray<OperandConstraint>(static_cast<int>(operand_count)); | 66 zone->NewArray<OperandConstraint>(static_cast<int>(operand_count)); |
25 // Construct OperandConstraints for all InstructionOperands, eliminating | |
26 // kSameAsFirst along the way. | |
27 size_t count = 0; | 67 size_t count = 0; |
28 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { | 68 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { |
29 BuildConstraint(instr->InputAt(i), &op_constraints[count]); | 69 BuildConstraint(instr->InputAt(i), &op_constraints[count]); |
30 CHECK_NE(kSameAsFirst, op_constraints[count].type_); | 70 VerifyInput(op_constraints[count]); |
71 } | |
72 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { | |
73 BuildConstraint(instr->TempAt(i), &op_constraints[count]); | |
74 VerifyTemp(op_constraints[count]); | |
31 } | 75 } |
32 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { | 76 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { |
33 BuildConstraint(instr->OutputAt(i), &op_constraints[count]); | 77 BuildConstraint(instr->OutputAt(i), &op_constraints[count]); |
34 if (op_constraints[count].type_ == kSameAsFirst) { | 78 if (op_constraints[count].type_ == kSameAsFirst) { |
35 CHECK(instr->InputCount() > 0); | 79 CHECK(instr->InputCount() > 0); |
36 op_constraints[count] = op_constraints[0]; | 80 op_constraints[count].type_ = op_constraints[0].type_; |
81 op_constraints[count].value_ = op_constraints[0].value_; | |
37 } | 82 } |
38 } | 83 VerifyOutput(op_constraints[count]); |
39 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { | |
40 BuildConstraint(instr->TempAt(i), &op_constraints[count]); | |
41 CHECK_NE(kSameAsFirst, op_constraints[count].type_); | |
42 } | 84 } |
43 // All gaps should be totally unallocated at this point. | 85 // All gaps should be totally unallocated at this point. |
44 if (instr->IsGapMoves()) { | 86 if (instr->IsGapMoves()) { |
45 const auto* gap = GapInstruction::cast(instr); | 87 CHECK(operand_count == 0); |
46 for (int i = GapInstruction::FIRST_INNER_POSITION; | 88 VerifyGapEmpty(GapInstruction::cast(instr)); |
47 i <= GapInstruction::LAST_INNER_POSITION; i++) { | |
48 GapInstruction::InnerPosition inner_pos = | |
49 static_cast<GapInstruction::InnerPosition>(i); | |
50 CHECK_EQ(NULL, gap->GetParallelMove(inner_pos)); | |
51 } | |
52 } | 89 } |
53 InstructionConstraint instr_constraint = {instr, operand_count, | 90 InstructionConstraint instr_constraint = {instr, operand_count, |
54 op_constraints}; | 91 op_constraints}; |
55 constraints()->push_back(instr_constraint); | 92 constraints()->push_back(instr_constraint); |
56 } | 93 } |
57 } | 94 } |
58 | 95 |
59 | 96 |
60 void RegisterAllocatorVerifier::VerifyAssignment() { | 97 void RegisterAllocatorVerifier::VerifyAssignment() { |
61 CHECK(sequence()->instructions().size() == constraints()->size()); | 98 CHECK(sequence()->instructions().size() == constraints()->size()); |
62 auto instr_it = sequence()->begin(); | 99 auto instr_it = sequence()->begin(); |
63 for (const auto& instr_constraint : *constraints()) { | 100 for (const auto& instr_constraint : *constraints()) { |
64 const auto* instr = instr_constraint.instruction_; | 101 const auto* instr = instr_constraint.instruction_; |
65 const size_t operand_count = instr_constraint.operand_constaints_size_; | 102 const size_t operand_count = instr_constraint.operand_constaints_size_; |
66 const auto* op_constraints = instr_constraint.operand_constraints_; | 103 const auto* op_constraints = instr_constraint.operand_constraints_; |
67 CHECK_EQ(instr, *instr_it); | 104 CHECK_EQ(instr, *instr_it); |
68 CHECK(operand_count == OperandCount(instr)); | 105 CHECK(operand_count == OperandCount(instr)); |
69 size_t count = 0; | 106 size_t count = 0; |
70 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { | 107 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { |
71 CheckConstraint(instr->InputAt(i), &op_constraints[count]); | 108 CheckConstraint(instr->InputAt(i), &op_constraints[count]); |
72 } | 109 } |
110 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { | |
111 CheckConstraint(instr->TempAt(i), &op_constraints[count]); | |
112 } | |
73 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { | 113 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { |
74 CheckConstraint(instr->OutputAt(i), &op_constraints[count]); | 114 CheckConstraint(instr->OutputAt(i), &op_constraints[count]); |
75 } | 115 } |
76 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { | |
77 CheckConstraint(instr->TempAt(i), &op_constraints[count]); | |
78 } | |
79 ++instr_it; | 116 ++instr_it; |
80 } | 117 } |
81 } | 118 } |
82 | 119 |
83 | 120 |
84 void RegisterAllocatorVerifier::BuildConstraint(const InstructionOperand* op, | 121 void RegisterAllocatorVerifier::BuildConstraint(const InstructionOperand* op, |
85 OperandConstraint* constraint) { | 122 OperandConstraint* constraint) { |
86 constraint->value_ = kMinInt; | 123 constraint->value_ = kMinInt; |
124 constraint->virtual_register_ = UnallocatedOperand::kInvalidVirtualRegister; | |
87 if (op->IsConstant()) { | 125 if (op->IsConstant()) { |
88 constraint->type_ = kConstant; | 126 constraint->type_ = kConstant; |
89 constraint->value_ = ConstantOperand::cast(op)->index(); | 127 constraint->value_ = ConstantOperand::cast(op)->index(); |
128 constraint->virtual_register_ = constraint->value_; | |
90 } else if (op->IsImmediate()) { | 129 } else if (op->IsImmediate()) { |
91 constraint->type_ = kImmediate; | 130 constraint->type_ = kImmediate; |
92 constraint->value_ = ImmediateOperand::cast(op)->index(); | 131 constraint->value_ = ImmediateOperand::cast(op)->index(); |
93 } else { | 132 } else { |
94 CHECK(op->IsUnallocated()); | 133 CHECK(op->IsUnallocated()); |
95 const auto* unallocated = UnallocatedOperand::cast(op); | 134 const auto* unallocated = UnallocatedOperand::cast(op); |
96 int vreg = unallocated->virtual_register(); | 135 int vreg = unallocated->virtual_register(); |
136 constraint->virtual_register_ = vreg; | |
97 if (unallocated->basic_policy() == UnallocatedOperand::FIXED_SLOT) { | 137 if (unallocated->basic_policy() == UnallocatedOperand::FIXED_SLOT) { |
98 constraint->type_ = kFixedSlot; | 138 constraint->type_ = kFixedSlot; |
99 constraint->value_ = unallocated->fixed_slot_index(); | 139 constraint->value_ = unallocated->fixed_slot_index(); |
100 } else { | 140 } else { |
101 switch (unallocated->extended_policy()) { | 141 switch (unallocated->extended_policy()) { |
102 case UnallocatedOperand::ANY: | 142 case UnallocatedOperand::ANY: |
103 CHECK(false); | 143 CHECK(false); |
104 break; | 144 break; |
105 case UnallocatedOperand::NONE: | 145 case UnallocatedOperand::NONE: |
106 if (sequence()->IsDouble(vreg)) { | 146 if (sequence()->IsDouble(vreg)) { |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
167 return; | 207 return; |
168 case kNoneDouble: | 208 case kNoneDouble: |
169 CHECK(op->IsDoubleRegister() || op->IsDoubleStackSlot()); | 209 CHECK(op->IsDoubleRegister() || op->IsDoubleStackSlot()); |
170 return; | 210 return; |
171 case kSameAsFirst: | 211 case kSameAsFirst: |
172 CHECK(false); | 212 CHECK(false); |
173 return; | 213 return; |
174 } | 214 } |
175 } | 215 } |
176 | 216 |
217 | |
218 namespace { | |
219 | |
220 struct OperandLess { | |
Jarin
2014/11/12 08:13:35
Just use a function here, no need to use a functor
dcarney
2014/11/12 08:53:31
std::map seemed to want this...
| |
221 bool operator()(const InstructionOperand* a, | |
222 const InstructionOperand* b) const { | |
223 if (a->kind() == b->kind()) return a->index() < b->index(); | |
224 return a->kind() < b->kind(); | |
225 } | |
226 }; | |
227 | |
228 | |
229 typedef std::map< | |
230 const InstructionOperand*, int, OperandLess, | |
231 zone_allocator<std::pair<const InstructionOperand*, const int>>> | |
232 LocationMap; | |
233 | |
234 | |
235 struct OutgoingMapping : ZoneObject { | |
236 explicit OutgoingMapping(Zone* zone) | |
237 : locations_(LocationMap::key_compare(), | |
238 LocationMap::allocator_type(zone)) {} | |
239 | |
240 void RunPhis(const InstructionBlock* block, const BlockStartInstruction* gap, | |
241 size_t index) { | |
242 // The first moves in the BlockStartInstruction are the phi moves inserted | |
243 // by ResolvePhis. | |
244 const ParallelMove* move = gap->GetParallelMove(GapInstruction::START); | |
245 CHECK_NE(nullptr, move); | |
246 const auto* move_ops = move->move_operands(); | |
247 CHECK(block->phis().size() <= static_cast<size_t>(move_ops->length())); | |
248 auto move_it = move_ops->begin(); | |
249 for (const auto* phi : block->phis()) { | |
250 const auto* op = move_it->source(); | |
251 auto it = locations_.find(op); | |
252 CHECK(it != locations_.end()); | |
253 CHECK_EQ(it->second, phi->operands()[index]); | |
254 it->second = phi->virtual_register(); | |
255 ++move_it; | |
256 } | |
257 } | |
258 | |
259 void RunGapInstruction(Zone* zone, const GapInstruction* gap) { | |
260 for (int i = GapInstruction::FIRST_INNER_POSITION; | |
261 i <= GapInstruction::LAST_INNER_POSITION; i++) { | |
262 GapInstruction::InnerPosition inner_pos = | |
263 static_cast<GapInstruction::InnerPosition>(i); | |
264 const ParallelMove* move = gap->GetParallelMove(inner_pos); | |
265 if (move == nullptr) continue; | |
266 RunParallelMoves(zone, move); | |
267 } | |
268 } | |
269 | |
270 void RunParallelMoves(Zone* zone, const ParallelMove* move) { | |
271 // Compute outgoing mappings. | |
272 LocationMap to_insert((LocationMap::key_compare()), | |
273 LocationMap::allocator_type(zone)); | |
274 auto* moves = move->move_operands(); | |
275 for (auto i = moves->begin(); i != moves->end(); ++i) { | |
276 if (i->IsEliminated()) continue; | |
277 CHECK(i->source()->kind() != InstructionOperand::INVALID); | |
278 auto cur = locations_.find(i->source()); | |
279 CHECK(cur != locations_.end()); | |
280 if (i->destination()->kind() == InstructionOperand::INVALID) continue; | |
281 to_insert.insert(std::make_pair(i->destination(), cur->second)); | |
282 } | |
283 // Drop current mappings. | |
284 for (auto i = moves->begin(); i != moves->end(); ++i) { | |
285 if (i->IsEliminated()) continue; | |
286 auto cur = locations_.find(i->destination()); | |
287 if (cur != locations_.end()) locations_.erase(cur); | |
288 } | |
289 // Insert new values. | |
290 locations_.insert(to_insert.begin(), to_insert.end()); | |
291 } | |
292 | |
293 void Map(const InstructionOperand* op, int virtual_register) { | |
294 locations_.insert(std::make_pair(op, virtual_register)); | |
295 } | |
296 | |
297 void Drop(const InstructionOperand* op) { | |
298 auto it = locations_.find(op); | |
299 if (it != locations_.end()) locations_.erase(it); | |
300 } | |
301 | |
302 void DropRegisters(const RegisterConfiguration* config) { | |
303 for (int i = 0; i < config->num_general_registers(); ++i) { | |
304 InstructionOperand op(InstructionOperand::REGISTER, i); | |
305 Drop(&op); | |
306 } | |
307 for (int i = 0; i < config->num_double_registers(); ++i) { | |
308 InstructionOperand op(InstructionOperand::DOUBLE_REGISTER, i); | |
309 Drop(&op); | |
310 } | |
311 } | |
312 | |
313 LocationMap locations_; | |
314 }; | |
315 | |
316 } // namespace | |
317 | |
318 | |
Jarin
2014/11/12 08:13:35
Could we have a comment describing what we verify
dcarney
2014/11/12 08:53:32
Done.
| |
319 void RegisterAllocatorVerifier::VerifyGapMoves() { | |
320 typedef ZoneVector<OutgoingMapping*> OutgoingMappings; | |
321 OutgoingMappings outgoing_mappings( | |
322 static_cast<int>(sequence()->instruction_blocks().size()), nullptr, | |
323 zone()); | |
324 // compute the locations of all virtual registers leaving every block, using | |
325 // only the first predecessor as the input locations. | |
326 int instr_index = 0; | |
327 size_t block_index = 0; | |
328 const auto* block = sequence()->instruction_blocks()[block_index]; | |
329 for (const auto& instr_constraint : *constraints()) { | |
330 if (block->code_end() == instr_index) { | |
331 block_index++; | |
332 block = sequence()->instruction_blocks()[block_index]; | |
333 } | |
334 auto* outgoing = outgoing_mappings[block_index]; | |
Jarin
2014/11/12 08:13:35
Perhaps rename outgoing => current.
dcarney
2014/11/12 08:53:31
Done.
| |
335 if (outgoing == nullptr) { | |
336 outgoing = new (zone()) OutgoingMapping(zone()); | |
337 outgoing_mappings[block_index] = outgoing; | |
338 // Copy outgoing values from predecessor block. | |
339 if (!block->predecessors().empty()) { | |
340 size_t predecessor_index = block->predecessors()[0].ToSize(); | |
Jarin
2014/11/12 08:13:35
Could you add the comment here that explains that
dcarney
2014/11/12 08:53:31
Done.
| |
341 CHECK(predecessor_index < block_index); | |
342 auto* incoming = outgoing_mappings[predecessor_index]; | |
Jarin
2014/11/12 08:13:35
Why is this working with loops? Is it because the
dcarney
2014/11/12 08:53:31
correct
| |
343 if (block->PredecessorCount() > 1) { | |
344 // Update incoming map with phis. Valid because of edge split form. | |
Jarin
2014/11/12 08:13:35
Why do not we copy the incoming to the outgoing an
dcarney
2014/11/12 08:53:31
I need to do this to ensure that I can do RunPhis
| |
345 CHECK(sequence() | |
346 ->instruction_blocks()[predecessor_index] | |
347 ->SuccessorCount() == 1); | |
348 const auto* start = | |
349 BlockStartInstruction::cast(instr_constraint.instruction_); | |
350 incoming->RunPhis(block, start, 0); | |
351 } | |
352 // Now initialize outgoing mapping for this block with incoming mapping. | |
353 outgoing->locations_.insert(incoming->locations_.begin(), | |
354 incoming->locations_.end()); | |
355 } | |
356 } | |
357 // Update map with gaps and instruction operands. | |
358 const auto* instr = instr_constraint.instruction_; | |
359 const auto* op_constraints = instr_constraint.operand_constraints_; | |
360 size_t count = 0; | |
361 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { | |
362 if (op_constraints[count].type_ == kImmediate) continue; | |
363 auto it = outgoing->locations_.find(instr->InputAt(i)); | |
364 int virtual_register = op_constraints[count].virtual_register_; | |
365 CHECK(it != outgoing->locations_.end()); | |
366 CHECK_EQ(it->second, virtual_register); | |
367 } | |
368 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { | |
369 outgoing->Drop(instr->TempAt(i)); | |
370 } | |
371 if (instr->IsCall()) { | |
372 outgoing->DropRegisters(config()); | |
373 } | |
374 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { | |
375 outgoing->Drop(instr->OutputAt(i)); | |
376 int virtual_register = op_constraints[count].virtual_register_; | |
377 outgoing->Map(instr->OutputAt(i), virtual_register); | |
378 } | |
379 if (instr->IsGapMoves()) { | |
380 const auto* gap = GapInstruction::cast(instr); | |
381 outgoing->RunGapInstruction(zone(), gap); | |
382 } | |
383 ++instr_index; | |
384 } | |
385 CHECK(++block_index == sequence()->instruction_blocks().size()); | |
386 // Run all remaining phis. | |
387 for (const auto* block : sequence()->instruction_blocks()) { | |
388 if (block->predecessors().size() <= 1) continue; | |
389 const auto* start = BlockStartInstruction::cast( | |
390 sequence()->InstructionAt(block->code_start())); | |
391 for (size_t pred_index = 1; pred_index < block->predecessors().size(); | |
392 ++pred_index) { | |
393 size_t pred_block_index = block->predecessors()[pred_index].ToSize(); | |
394 auto* mapping = outgoing_mappings[pred_block_index]; | |
395 CHECK(sequence() | |
396 ->instruction_blocks()[pred_block_index] | |
397 ->SuccessorCount() == 1); | |
398 mapping->RunPhis(block, start, pred_index); | |
399 // TODO(dcarney): run a verification that this mapping is the same as the | |
Jarin
2014/11/12 08:13:35
Yeah, it would be nice if we could apply the phis
dcarney
2014/11/12 08:53:31
I don't want to use this register allocator if I c
| |
400 // mapping for the first predecessor w.r.t live values. | |
401 } | |
402 } | |
403 } | |
404 | |
177 } // namespace compiler | 405 } // namespace compiler |
178 } // namespace internal | 406 } // namespace internal |
179 } // namespace v8 | 407 } // namespace v8 |
OLD | NEW |