|
23 | 23 | /// 2. Find the "original" boundary of liveness using
|
24 | 24 | /// PrunedLiveness::computeBoundary.
|
25 | 25 | ///
|
26 |
| -/// 3. Find the "extended" boundary of liveness by walking out from the boundary |
| 26 | +/// 3. (Optional) At Onone, extend liveness up to original extent when possible |
| 27 | +/// without incurring extra copies. |
| 28 | +/// |
| 29 | +/// 4. Find the "extended" boundary of liveness by walking out from the boundary |
27 | 30 | /// computed by PrunedLiveness out to destroys which aren't separated from
|
28 | 31 | /// the original destory by "interesting" instructions.
|
29 | 32 | ///
|
30 |
| -/// 4. Initializes `consumes` and inserts new destroy_value instructions. |
| 33 | +/// 5. Initializes `consumes` and inserts new destroy_value instructions. |
31 | 34 | ///
|
32 |
| -/// 5. Rewrite `def`s original copies and destroys, inserting new copies where |
| 35 | +/// 6. Rewrite `def`s original copies and destroys, inserting new copies where |
33 | 36 | /// needed. Deletes original copies and destroys and inserts new copies.
|
34 | 37 | ///
|
35 | 38 | /// See CanonicalizeOSSALifetime.h for examples.
|
|
55 | 58 | ///
|
56 | 59 | /// 2. Liveness is extended out to original destroys to avoid spurious changes.
|
57 | 60 | ///
|
| 61 | +/// 3. In the Onone mode, liveness is preserved to its previous extent whenever |
| 62 | +/// doing so doesn't incur extra copies compared to what is done in the O mode. |
| 63 | +/// |
58 | 64 | //===----------------------------------------------------------------------===//
|
59 | 65 |
|
60 | 66 | #define DEBUG_TYPE "copy-propagation"
|
@@ -425,7 +431,134 @@ void CanonicalizeOSSALifetime::findOriginalBoundary(
|
425 | 431 | }
|
426 | 432 |
|
427 | 433 | //===----------------------------------------------------------------------===//
|
428 |
| -// MARK: Step 3. Extend the "original" boundary from step 2 up to destroys that |
| 434 | +// MARK: Step 3. (Optional) Maximize lifetimes. |
| 435 | +//===----------------------------------------------------------------------===// |
| 436 | + |
| 437 | +/// At -Onone, there are some conflicting goals: |
| 438 | +/// On the one hand: good debugging experience. |
| 439 | +/// (1) do not shorten value's lifetime |
| 440 | +/// On the other: demonstrate semantics. |
| 441 | +/// (2) consume value at same places it will be consumed at -O |
| 442 | +/// (3) ensure there are no more copies than there would be at -O |
| 443 | +/// |
| 444 | +/// (2) and (3) are equivalent--extra (compared to -O) copies arise from failing |
| 445 | +/// to end lifetimes at consuming uses (which then need their own copies). |
| 446 | +/// |
| 447 | +/// We achieve (2) and (3) always. We achieve (1) where possible. |
| 448 | +/// |
| 449 | +/// Conceptually, the strategy is the following: |
| 450 | +/// - Collect the blocks in which the value was live before canonicalization. |
| 451 | +/// These are the "original" live blocks (originalLiveBlocks). |
| 452 | +/// [Color these blocks green.] |
| 453 | +/// - From within that collection, collect the blocks which contain a _final_ |
| 454 | +/// consuming, non-destroy use, and their successors. |
| 455 | +/// These are the "consumed" blocks (consumedAtExitBlocks). |
| 456 | +/// [Color these blocks red.] |
| 457 | +/// - Extend liveness down to the boundary between originalLiveBlocks and |
| 458 | +/// consumedAtExitBlocks blocks. |
| 459 | +/// [Extend liveness down to the boundary between green blocks and red.] |
| 460 | +/// - In particular, in regions of originalLiveBlocks which have no boundary |
| 461 | +/// with consumedAtExitBlocks, liveness should be extended to its original |
| 462 | +/// extent. |
| 463 | +/// [Extend liveness down to the boundary between green blocks and uncolored.] |
| 464 | +void CanonicalizeOSSALifetime::extendUnconsumedLiveness( |
| 465 | + PrunedLivenessBoundary const &boundary) { |
| 466 | + auto currentDef = getCurrentDef(); |
| 467 | + |
| 468 | + // First, collect the blocks that were _originally_ live. We can't use |
| 469 | + // liveness here because it doesn't include blocks that occur before a |
| 470 | + // destroy_value. |
| 471 | + BasicBlockSet originalLiveBlocks(currentDef->getFunction()); |
| 472 | + { |
| 473 | + // Some of the work here was already done by computeCanonicalLiveness. |
| 474 | + // Specifically, it already discovered all blocks containing (transitive) |
| 475 | + // uses and blocks that appear between them and the def. |
| 476 | + // |
| 477 | + // Seed the set with what it already discovered. |
| 478 | + for (auto *discoveredBlock : liveness.getDiscoveredBlocks()) |
| 479 | + originalLiveBlocks.insert(discoveredBlock); |
| 480 | + |
| 481 | + // Start the walk from the consuming blocks (which includes destroys as well |
| 482 | + // as the other consuming uses). |
| 483 | + BasicBlockWorklist worklist(currentDef->getFunction()); |
| 484 | + for (auto *consumingBlock : consumingBlocks) { |
| 485 | + worklist.push(consumingBlock); |
| 486 | + } |
| 487 | + |
| 488 | + // Walk backwards from consuming blocks. |
| 489 | + while (auto *block = worklist.pop()) { |
| 490 | + originalLiveBlocks.insert(block); |
| 491 | + for (auto *predecessor : block->getPredecessorBlocks()) { |
| 492 | + // If the block was discovered by liveness, we already added it to the |
| 493 | + // set. |
| 494 | + if (originalLiveBlocks.contains(predecessor)) |
| 495 | + continue; |
| 496 | + worklist.pushIfNotVisited(predecessor); |
| 497 | + } |
| 498 | + } |
| 499 | + } |
| 500 | + |
| 501 | + // Second, collect the blocks which occur after a _final_ consuming use. |
| 502 | + BasicBlockSet consumedAtExitBlocks(currentDef->getFunction()); |
| 503 | + StackList<SILBasicBlock *> consumedAtEntryBlocks(currentDef->getFunction()); |
| 504 | + { |
| 505 | + // Start the forward walk from blocks which contain _final_ non-destroy |
| 506 | + // consumes. These are just the instructions on the boundary which aren't |
| 507 | + // destroys. |
| 508 | + BasicBlockWorklist worklist(currentDef->getFunction()); |
| 509 | + for (auto *instruction : boundary.lastUsers) { |
| 510 | + if (dynCastToDestroyOf(instruction, getCurrentDef())) |
| 511 | + continue; |
| 512 | + if (liveness.isInterestingUser(instruction) != |
| 513 | + PrunedLiveness::IsInterestingUser::LifetimeEndingUse) |
| 514 | + continue; |
| 515 | + worklist.push(instruction->getParent()); |
| 516 | + } |
| 517 | + while (auto *block = worklist.pop()) { |
| 518 | + consumedAtExitBlocks.insert(block); |
| 519 | + for (auto *successor : block->getSuccessorBlocks()) { |
| 520 | + if (!originalLiveBlocks.contains(successor)) |
| 521 | + continue; |
| 522 | + worklist.pushIfNotVisited(successor); |
| 523 | + consumedAtEntryBlocks.push_back(successor); |
| 524 | + } |
| 525 | + } |
| 526 | + } |
| 527 | + |
| 528 | + // Third, find the blocks on the boundary between the originally-live blocks |
| 529 | + // and the originally-live-but-consumed blocks. Extend liveness "to the end" |
| 530 | + // of these blocks. |
| 531 | + for (auto *block : consumedAtEntryBlocks) { |
| 532 | + for (auto *predecessor : block->getPredecessorBlocks()) { |
| 533 | + if (consumedAtExitBlocks.contains(predecessor)) |
| 534 | + continue; |
| 535 | + // Add "the instruction(s) before the terminator" of the predecessor to |
| 536 | + // liveness. |
| 537 | + if (auto *inst = predecessor->getTerminator()->getPreviousInstruction()) { |
| 538 | + liveness.updateForUse(inst, /*lifetimeEnding*/ false); |
| 539 | + } else { |
| 540 | + for (auto *grandPredecessor : predecessor->getPredecessorBlocks()) { |
| 541 | + liveness.updateForUse(grandPredecessor->getTerminator(), |
| 542 | + /*lifetimeEnding*/ false); |
| 543 | + } |
| 544 | + } |
| 545 | + } |
| 546 | + } |
| 547 | + |
| 548 | + // Finally, preserve the destroys which weren't in the consumed region in |
| 549 | + // place: hoisting such destroys would not avoid copies. |
| 550 | + for (auto *destroy : destroys) { |
| 551 | + auto *block = destroy->getParent(); |
| 552 | + // If the destroy is in a consumed block or a final consuming block, |
| 553 | + // hoisting it would avoid a copy. |
| 554 | + if (consumedAtExitBlocks.contains(block)) |
| 555 | + continue; |
| 556 | + liveness.updateForUse(destroy, /*lifetimeEnding*/ true); |
| 557 | + } |
| 558 | +} |
| 559 | + |
| 560 | +//===----------------------------------------------------------------------===// |
| 561 | +// MARK: Step 4. Extend the "original" boundary from step 2 up to destroys that |
429 | 562 | // aren't separated from it by "interesting" instructions.
|
430 | 563 | //===----------------------------------------------------------------------===//
|
431 | 564 |
|
@@ -639,7 +772,7 @@ void CanonicalizeOSSALifetime::findExtendedBoundary(
|
639 | 772 | }
|
640 | 773 |
|
641 | 774 | //===----------------------------------------------------------------------===//
|
642 |
| -// MARK: Step 4. Insert destroys onto the boundary found in step 3 where needed. |
| 775 | +// MARK: Step 5. Insert destroys onto the boundary found in step 3 where needed. |
643 | 776 | //===----------------------------------------------------------------------===//
|
644 | 777 |
|
645 | 778 | /// Create a new destroy_value instruction before the specified instruction and
|
@@ -729,7 +862,7 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
|
729 | 862 | }
|
730 | 863 |
|
731 | 864 | //===----------------------------------------------------------------------===//
|
732 |
| -// MARK: Step 5. Rewrite copies and destroys |
| 865 | +// MARK: Step 6. Rewrite copies and destroys |
733 | 866 | //===----------------------------------------------------------------------===//
|
734 | 867 |
|
735 | 868 | /// The lifetime extends beyond given consuming use. Copy the value.
|
@@ -898,12 +1031,24 @@ bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
|
898 | 1031 | // Step 2: compute original boundary
|
899 | 1032 | PrunedLivenessBoundary originalBoundary;
|
900 | 1033 | findOriginalBoundary(originalBoundary);
|
901 |
| - // Step 3: extend boundary to destroys |
902 | 1034 | PrunedLivenessBoundary boundary;
|
903 |
| - findExtendedBoundary(originalBoundary, boundary); |
904 |
| - // Step 4: insert destroys and record consumes |
| 1035 | + if (maximizeLifetime) { |
| 1036 | + // Step 3. (optional) maximize lifetimes |
| 1037 | + extendUnconsumedLiveness(originalBoundary); |
| 1038 | + originalBoundary.clear(); |
| 1039 | + // Step 2: (again) recompute the original boundary since we've extended |
| 1040 | + // liveness |
| 1041 | + findOriginalBoundary(originalBoundary); |
| 1042 | + // Step 4: extend boundary to destroys |
| 1043 | + findExtendedBoundary(originalBoundary, boundary); |
| 1044 | + } else { |
| 1045 | + // Step 3: (skipped) |
| 1046 | + // Step 4: extend boundary to destroys |
| 1047 | + findExtendedBoundary(originalBoundary, boundary); |
| 1048 | + } |
| 1049 | + // Step 5: insert destroys and record consumes |
905 | 1050 | insertDestroysOnBoundary(boundary);
|
906 |
| - // Step 5: rewrite copies and delete extra destroys |
| 1051 | + // Step 6: rewrite copies and delete extra destroys |
907 | 1052 | rewriteCopies();
|
908 | 1053 |
|
909 | 1054 | clearLiveness();
|
|
0 commit comments