|
52 | 52 | ///
|
53 | 53 | /// 2. Liveness is extended out to original destroys to avoid spurious changes.
|
54 | 54 | ///
|
| 55 | +/// 3. In the Onone mode, liveness is preserved to its previous extent whenever |
| 56 | +/// doing so doesn't incur extra copies compared to what is done in the O mode. |
| 57 | +/// |
55 | 58 | //===----------------------------------------------------------------------===//
|
56 | 59 |
|
57 | 60 | #define DEBUG_TYPE "copy-propagation"
|
@@ -611,6 +614,118 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
|
611 | 614 | }
|
612 | 615 | }
|
613 | 616 |
|
| 617 | +/// At -Onone, there are some conflicting goals: |
| 618 | +/// On the one hand: good debugging experience. |
| 619 | +/// (1) do not shorten value's lifetime |
| 620 | +/// On the other: demonstrate semantics. |
| 621 | +/// (2) consume value at same places it will be consumed at -O |
| 622 | +/// (3) ensure there are no more copies than there would be at -O |
| 623 | +/// |
| 624 | +/// (2) and (3) are equivalent--extra (compared to -O) copies arise from failing |
| 625 | +/// to end lifetimes at consuming uses (which then need their own copies). |
| 626 | +/// |
| 627 | +/// We achieve (2) and (3) always. We achieve (1) where possible. |
| 628 | +/// |
| 629 | +/// Conceptually, the strategy is the following: |
| 630 | +/// - Collect the blocks in which the value was live before canonicalization. |
| 631 | +/// These are the "original" live blocks (originalLiveBlocks). |
| 632 | +/// [Color these blocks green.] |
| 633 | +/// - From that collection, collect the blocks which occur after a _final_ |
| 634 | +/// consuming use (that isn't a destroy). |
| 635 | +/// These are the "consumed" blocks (consumedBlocks). |
| 636 | +/// [Color these blocks red.] |
| 637 | +/// - Extend liveness up to the boundary between originalLiveBlocks and |
| 638 | +/// consumedBlocks blocks. |
| 639 | +/// [Extend liveness up to the boundary between green blocks and red.] |
| 640 | +/// - In particular, in regions of originalLiveBlocks which have no boundary |
| 641 | +/// with consumedBlocks, livness should be extended to its original extent. |
| 642 | +/// [Extend liveness up to the boundary between green blocks and uncolored.] |
| 643 | +void CanonicalizeOSSALifetime::extendUnconsumedLiveness( |
| 644 | + PrunedLivenessBoundary &boundary) { |
| 645 | + auto currentDef = getCurrentDef(); |
| 646 | + |
| 647 | + // First, collect the blocks that were _originally_ live. We can't use |
| 648 | + // liveness here because it doesn't include blocks that occur before a |
| 649 | + // destroy_value. |
| 650 | + BasicBlockSet originalLiveBlocks(currentDef->getFunction()); |
| 651 | + { |
| 652 | + // Start the walk from the consuming blocks (which includes destroys as well |
| 653 | + // as the other consuming uses). |
| 654 | + BasicBlockWorklist worklist(currentDef->getFunction()); |
| 655 | + for (auto *consumingBlock : consumingBlocks) { |
| 656 | + worklist.push(consumingBlock); |
| 657 | + } |
| 658 | + |
| 659 | + // Walk backwards from consuming blocks. |
| 660 | + auto *currentDefBlock = currentDef->getParentBlock(); |
| 661 | + while (auto *block = worklist.pop()) { |
| 662 | + originalLiveBlocks.insert(block); |
| 663 | + // Keep adding blocks until we find the def block. |
| 664 | + if (currentDefBlock == block) |
| 665 | + continue; |
| 666 | + for (auto *predecessor : block->getPredecessorBlocks()) { |
| 667 | + worklist.pushIfNotVisited(predecessor); |
| 668 | + } |
| 669 | + } |
| 670 | + } |
| 671 | + |
| 672 | + // Second, collect the blocks which occur after a _final_ consuming use. |
| 673 | + BasicBlockSetVector finalConsumingBlocks(currentDef->getFunction()); |
| 674 | + BasicBlockSetVector consumedBlocks(currentDef->getFunction()); |
| 675 | + { |
| 676 | + // Start the walk from blocks which contain _final_ non-destroy consumes. |
| 677 | + // These are just our instructions on the boundary which aren't destroys. |
| 678 | + BasicBlockWorklist worklist(currentDef->getFunction()); |
| 679 | + for (auto *instruction : boundary.lastUsers) { |
| 680 | + if (dynCastToDestroyOf(instruction, getCurrentDef())) |
| 681 | + continue; |
| 682 | + if (liveness.isInterestingUser(instruction) != |
| 683 | + PrunedLiveness::IsInterestingUser::LifetimeEndingUse) |
| 684 | + continue; |
| 685 | + worklist.push(instruction->getParent()); |
| 686 | + } |
| 687 | + while (auto *block = worklist.pop()) { |
| 688 | + finalConsumingBlocks.insert(block); |
| 689 | + for (auto *successor : block->getSuccessorBlocks()) { |
| 690 | + if (!originalLiveBlocks.contains(successor)) |
| 691 | + continue; |
| 692 | + worklist.pushIfNotVisited(successor); |
| 693 | + consumedBlocks.insert(successor); |
| 694 | + } |
| 695 | + } |
| 696 | + } |
| 697 | + |
| 698 | + // Third, find the blocks on the boundary between the originally-live blocks |
| 699 | + // and the originally-live-but-consumed blocks. These are new boundary |
| 700 | + // blocks. |
| 701 | + for (auto *block : consumedBlocks) { |
| 702 | + for (auto *predecessor : block->getPredecessorBlocks()) { |
| 703 | + if (!consumedBlocks.contains(predecessor) && |
| 704 | + !finalConsumingBlocks.contains(predecessor)) { |
| 705 | + // Add "the instruction(s) before the terminator" of the predecessor to |
| 706 | + // liveness. |
| 707 | + if (auto *inst = |
| 708 | + predecessor->getTerminator()->getPreviousInstruction()) { |
| 709 | + liveness.updateForUse(inst, /*lifetimeEnding*/ false); |
| 710 | + } else { |
| 711 | + for (auto *grandPredecessor : predecessor->getPredecessorBlocks()) { |
| 712 | + liveness.updateForUse(grandPredecessor->getTerminator(), |
| 713 | + /*lifetimeEnding*/ false); |
| 714 | + } |
| 715 | + } |
| 716 | + } |
| 717 | + } |
| 718 | + } |
| 719 | + |
| 720 | + // Finally, preserve the destroys which weren't in the consumed region in |
| 721 | + // place: hoisting such destroys would not avoid copies. |
| 722 | + for (auto *destroy : destroys) { |
| 723 | + if (consumedBlocks.contains(destroy->getParent())) |
| 724 | + continue; |
| 725 | + liveness.updateForUse(destroy, /*lifetimeEnding*/ true); |
| 726 | + } |
| 727 | +} |
| 728 | + |
614 | 729 | //===----------------------------------------------------------------------===//
|
615 | 730 | // MARK: Step 4. Rewrite copies and destroys
|
616 | 731 | //===----------------------------------------------------------------------===//
|
@@ -781,6 +896,11 @@ bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
|
781 | 896 | // Step 2: compute boundary
|
782 | 897 | PrunedLivenessBoundary boundary;
|
783 | 898 | findExtendedBoundary(boundary);
|
| 899 | + if (ononeMode) { |
| 900 | + extendUnconsumedLiveness(boundary); |
| 901 | + boundary.clear(); |
| 902 | + findExtendedBoundary(boundary); |
| 903 | + } |
784 | 904 | // Step 3: insert destroys and record consumes
|
785 | 905 | insertDestroysOnBoundary(boundary);
|
786 | 906 | // Step 4: rewrite copies and delete extra destroys
|
|
0 commit comments