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/bit-vector.h" | 5 #include "src/bit-vector.h" |
6 #include "src/compiler/instruction.h" | 6 #include "src/compiler/instruction.h" |
7 #include "src/compiler/register-allocator-verifier.h" | 7 #include "src/compiler/register-allocator-verifier.h" |
8 | 8 |
9 namespace v8 { | 9 namespace v8 { |
10 namespace internal { | 10 namespace internal { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 } // namespace | 45 } // namespace |
46 | 46 |
47 RegisterAllocatorVerifier::RegisterAllocatorVerifier( | 47 RegisterAllocatorVerifier::RegisterAllocatorVerifier( |
48 Zone* zone, const RegisterConfiguration* config, | 48 Zone* zone, const RegisterConfiguration* config, |
49 const InstructionSequence* sequence) | 49 const InstructionSequence* sequence) |
50 : zone_(zone), | 50 : zone_(zone), |
51 config_(config), | 51 config_(config), |
52 sequence_(sequence), | 52 sequence_(sequence), |
53 constraints_(zone), | 53 constraints_(zone), |
54 assessments_(zone), | 54 assessments_(zone), |
55 outstanding_assessments_(zone), | 55 outstanding_assessments_(zone) { |
56 worklist_(zone), | |
57 seen_(zone) { | |
58 constraints_.reserve(sequence->instructions().size()); | 56 constraints_.reserve(sequence->instructions().size()); |
59 // TODO(dcarney): model unique constraints. | 57 // TODO(dcarney): model unique constraints. |
60 // Construct OperandConstraints for all InstructionOperands, eliminating | 58 // Construct OperandConstraints for all InstructionOperands, eliminating |
61 // kSameAsFirst along the way. | 59 // kSameAsFirst along the way. |
62 for (const Instruction* instr : sequence->instructions()) { | 60 for (const Instruction* instr : sequence->instructions()) { |
63 // All gaps should be totally unallocated at this point. | 61 // All gaps should be totally unallocated at this point. |
64 VerifyEmptyGaps(instr); | 62 VerifyEmptyGaps(instr); |
65 const size_t operand_count = OperandCount(instr); | 63 const size_t operand_count = OperandCount(instr); |
66 OperandConstraint* op_constraints = | 64 OperandConstraint* op_constraints = |
67 zone->NewArray<OperandConstraint>(operand_count); | 65 zone->NewArray<OperandConstraint>(operand_count); |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 ret->map().insert(std::make_pair( | 338 ret->map().insert(std::make_pair( |
341 operand, new (zone()) PendingAssessment(block, operand))); | 339 operand, new (zone()) PendingAssessment(block, operand))); |
342 } | 340 } |
343 } | 341 } |
344 } | 342 } |
345 } | 343 } |
346 return ret; | 344 return ret; |
347 } | 345 } |
348 | 346 |
349 void RegisterAllocatorVerifier::ValidatePendingAssessment( | 347 void RegisterAllocatorVerifier::ValidatePendingAssessment( |
350 RpoNumber block_id, InstructionOperand op, PendingAssessment* assessment, | 348 RpoNumber block_id, InstructionOperand op, |
| 349 BlockAssessments* current_assessments, const PendingAssessment* assessment, |
351 int virtual_register) { | 350 int virtual_register) { |
352 // When validating a pending assessment, it is possible some of the | 351 // When validating a pending assessment, it is possible some of the |
353 // assessments | 352 // assessments |
354 // for the original operand (the one where the assessment was created for | 353 // for the original operand (the one where the assessment was created for |
355 // first) are also pending. To avoid recursion, we use a work list. To | 354 // first) are also pending. To avoid recursion, we use a work list. To |
356 // deal with cycles, we keep a set of seen nodes. | 355 // deal with cycles, we keep a set of seen nodes. |
357 CHECK(worklist_.empty()); | 356 ZoneQueue<std::pair<const PendingAssessment*, int>> worklist(zone()); |
358 CHECK(seen_.empty()); | 357 ZoneSet<RpoNumber> seen(zone()); |
359 worklist_.push(std::make_pair(assessment, virtual_register)); | 358 worklist.push(std::make_pair(assessment, virtual_register)); |
360 seen_.insert(block_id); | 359 seen.insert(block_id); |
361 | 360 |
362 while (!worklist_.empty()) { | 361 while (!worklist.empty()) { |
363 auto work = worklist_.front(); | 362 auto work = worklist.front(); |
364 PendingAssessment* current_assessment = work.first; | 363 const PendingAssessment* current_assessment = work.first; |
365 int current_virtual_register = work.second; | 364 int current_virtual_register = work.second; |
366 InstructionOperand current_operand = current_assessment->operand(); | 365 InstructionOperand current_operand = current_assessment->operand(); |
367 worklist_.pop(); | 366 worklist.pop(); |
368 | 367 |
369 const InstructionBlock* origin = current_assessment->origin(); | 368 const InstructionBlock* origin = current_assessment->origin(); |
370 CHECK(origin->PredecessorCount() > 1 || origin->phis().size() > 0); | 369 CHECK(origin->PredecessorCount() > 1 || origin->phis().size() > 0); |
371 | 370 |
372 // Check if the virtual register is a phi first, instead of relying on | 371 // Check if the virtual register is a phi first, instead of relying on |
373 // the incoming assessments. In particular, this handles the case | 372 // the incoming assessments. In particular, this handles the case |
374 // v1 = phi v0 v0, which structurally is identical to v0 having been | 373 // v1 = phi v0 v0, which structurally is identical to v0 having been |
375 // defined at the top of a diamond, and arriving at the node joining the | 374 // defined at the top of a diamond, and arriving at the node joining the |
376 // diamond's branches. | 375 // diamond's branches. |
377 const PhiInstruction* phi = nullptr; | 376 const PhiInstruction* phi = nullptr; |
(...skipping 25 matching lines...) Expand all Loading... |
403 continue; | 402 continue; |
404 } | 403 } |
405 | 404 |
406 const BlockAssessments* pred_assessments = pred_assignment->second; | 405 const BlockAssessments* pred_assessments = pred_assignment->second; |
407 auto found_contribution = pred_assessments->map().find(current_operand); | 406 auto found_contribution = pred_assessments->map().find(current_operand); |
408 CHECK(found_contribution != pred_assessments->map().end()); | 407 CHECK(found_contribution != pred_assessments->map().end()); |
409 Assessment* contribution = found_contribution->second; | 408 Assessment* contribution = found_contribution->second; |
410 | 409 |
411 switch (contribution->kind()) { | 410 switch (contribution->kind()) { |
412 case Final: | 411 case Final: |
413 CHECK_EQ(FinalAssessment::cast(contribution)->virtual_register(), | 412 ValidateFinalAssessment( |
414 expected); | 413 block_id, current_operand, current_assessments, |
| 414 FinalAssessment::cast(contribution), expected); |
415 break; | 415 break; |
416 case Pending: { | 416 case Pending: { |
417 // This happens if we have a diamond feeding into another one, and | 417 // This happens if we have a diamond feeding into another one, and |
418 // the inner one never being used - other than for carrying the value. | 418 // the inner one never being used - other than for carrying the value. |
419 PendingAssessment* next = PendingAssessment::cast(contribution); | 419 PendingAssessment* next = PendingAssessment::cast(contribution); |
420 if (seen_.find(pred) == seen_.end()) { | 420 if (seen.find(pred) == seen.end()) { |
421 worklist_.push({next, expected}); | 421 worklist.push({next, expected}); |
422 seen_.insert(pred); | 422 seen.insert(pred); |
423 } | 423 } |
424 // Note that we do not want to finalize pending assessments at the | 424 // Note that we do not want to finalize pending assessments at the |
425 // beginning of a block - which is the information we'd have | 425 // beginning of a block - which is the information we'd have |
426 // available here. This is because this operand may be reused to | 426 // available here. This is because this operand may be reused to |
427 // define | 427 // define |
428 // duplicate phis. | 428 // duplicate phis. |
429 break; | 429 break; |
430 } | 430 } |
431 } | 431 } |
432 } | 432 } |
433 } | 433 } |
434 seen_.clear(); | 434 // If everything checks out, we may make the assessment. |
435 CHECK(worklist_.empty()); | 435 current_assessments->map()[op] = |
| 436 new (zone()) FinalAssessment(virtual_register, assessment); |
| 437 } |
| 438 |
| 439 void RegisterAllocatorVerifier::ValidateFinalAssessment( |
| 440 RpoNumber block_id, InstructionOperand op, |
| 441 BlockAssessments* current_assessments, const FinalAssessment* assessment, |
| 442 int virtual_register) { |
| 443 if (assessment->virtual_register() == virtual_register) return; |
| 444 // If we have 2 phis with the exact same operand list, and the first phi is |
| 445 // used before the second one, via the operand incoming to the block, |
| 446 // and the second one's operand is defined (via a parallel move) after the |
| 447 // use, then the original operand will be assigned to the first phi. We |
| 448 // then look at the original pending assessment to ascertain if op |
| 449 // is virtual_register. |
| 450 const PendingAssessment* old = assessment->original_pending_assessment(); |
| 451 CHECK_NOT_NULL(old); |
| 452 ValidatePendingAssessment(block_id, op, current_assessments, old, |
| 453 virtual_register); |
436 } | 454 } |
437 | 455 |
438 void RegisterAllocatorVerifier::ValidateUse( | 456 void RegisterAllocatorVerifier::ValidateUse( |
439 RpoNumber block_id, BlockAssessments* current_assessments, | 457 RpoNumber block_id, BlockAssessments* current_assessments, |
440 InstructionOperand op, int virtual_register) { | 458 InstructionOperand op, int virtual_register) { |
441 auto iterator = current_assessments->map().find(op); | 459 auto iterator = current_assessments->map().find(op); |
442 // We should have seen this operand before. | 460 // We should have seen this operand before. |
443 CHECK(iterator != current_assessments->map().end()); | 461 CHECK(iterator != current_assessments->map().end()); |
444 Assessment* assessment = iterator->second; | 462 Assessment* assessment = iterator->second; |
445 | 463 |
446 switch (assessment->kind()) { | 464 switch (assessment->kind()) { |
447 case Final: | 465 case Final: |
448 // The virtual registers should match. | 466 ValidateFinalAssessment(block_id, op, current_assessments, |
449 CHECK_EQ(FinalAssessment::cast(assessment)->virtual_register(), | 467 FinalAssessment::cast(assessment), |
450 virtual_register); | 468 virtual_register); |
451 break; | 469 break; |
452 case Pending: { | 470 case Pending: { |
453 ValidatePendingAssessment( | 471 const PendingAssessment* pending = PendingAssessment::cast(assessment); |
454 block_id, op, PendingAssessment::cast(assessment), virtual_register); | 472 ValidatePendingAssessment(block_id, op, current_assessments, pending, |
455 // If everything checks out, we may make the assessment. | 473 virtual_register); |
456 current_assessments->map()[op] = | |
457 new (zone()) FinalAssessment(virtual_register); | |
458 break; | 474 break; |
459 } | 475 } |
460 } | 476 } |
461 } | 477 } |
462 | 478 |
463 void RegisterAllocatorVerifier::VerifyGapMoves() { | 479 void RegisterAllocatorVerifier::VerifyGapMoves() { |
464 CHECK(assessments_.empty()); | 480 CHECK(assessments_.empty()); |
465 CHECK(outstanding_assessments_.empty()); | 481 CHECK(outstanding_assessments_.empty()); |
466 const size_t block_count = sequence()->instruction_blocks().size(); | 482 const size_t block_count = sequence()->instruction_blocks().size(); |
467 for (size_t block_index = 0; block_index < block_count; ++block_index) { | 483 for (size_t block_index = 0; block_index < block_count; ++block_index) { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
515 auto todo_iter = outstanding_assessments_.find(block->rpo_number()); | 531 auto todo_iter = outstanding_assessments_.find(block->rpo_number()); |
516 if (todo_iter == outstanding_assessments_.end()) continue; | 532 if (todo_iter == outstanding_assessments_.end()) continue; |
517 DelayedAssessments* todo = todo_iter->second; | 533 DelayedAssessments* todo = todo_iter->second; |
518 for (auto pair : todo->map()) { | 534 for (auto pair : todo->map()) { |
519 InstructionOperand op = pair.first; | 535 InstructionOperand op = pair.first; |
520 int vreg = pair.second; | 536 int vreg = pair.second; |
521 auto found_op = block_assessments->map().find(op); | 537 auto found_op = block_assessments->map().find(op); |
522 CHECK(found_op != block_assessments->map().end()); | 538 CHECK(found_op != block_assessments->map().end()); |
523 switch (found_op->second->kind()) { | 539 switch (found_op->second->kind()) { |
524 case Final: | 540 case Final: |
525 CHECK(FinalAssessment::cast(found_op->second)->virtual_register() == | 541 ValidateFinalAssessment(block->rpo_number(), op, block_assessments, |
526 vreg); | 542 FinalAssessment::cast(found_op->second), |
| 543 vreg); |
527 break; | 544 break; |
528 case Pending: | 545 case Pending: |
529 ValidatePendingAssessment(block->rpo_number(), op, | 546 const PendingAssessment* pending = |
530 PendingAssessment::cast(found_op->second), | 547 PendingAssessment::cast(found_op->second); |
531 vreg); | 548 ValidatePendingAssessment(block->rpo_number(), op, block_assessments, |
532 block_assessments->map()[op] = new (zone()) FinalAssessment(vreg); | 549 pending, vreg); |
| 550 block_assessments->map()[op] = |
| 551 new (zone()) FinalAssessment(vreg, pending); |
533 break; | 552 break; |
534 } | 553 } |
535 } | 554 } |
536 } | 555 } |
537 } | 556 } |
538 | 557 |
539 } // namespace compiler | 558 } // namespace compiler |
540 } // namespace internal | 559 } // namespace internal |
541 } // namespace v8 | 560 } // namespace v8 |
OLD | NEW |