Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Side by Side Diff: src/compiler/register-allocator-verifier.cc

Issue 704193007: [turbofan] add gap move verifier (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698