@@ -2383,12 +2383,15 @@ bool swift::isNestedLexicalBeginBorrow(BeginBorrowInst *bbi) {
2383
2383
}
2384
2384
2385
2385
bool swift::isRedundantMoveValue (MoveValueInst *mvi) {
2386
- // Check whether the moved-from value's lifetime and the moved-to value's
2386
+ // Given: %moved_to_value = move_value %original_value
2387
+ //
2388
+ // Check whether the original value's lifetime and the moved-to value's
2387
2389
// lifetime have the same (1) ownership, (2) lexicality, and (3) escaping.
2388
2390
//
2389
2391
// Along the way, also check for cases where they have different values for
2390
2392
// those characteristics but it doesn't matter because of how limited the uses
2391
- // of the original value are (for now, whether the move is the only use).
2393
+ // of the original value are (for now, whether the move is the only consuming
2394
+ // use).
2392
2395
2393
2396
auto original = mvi->getOperand ();
2394
2397
@@ -2405,42 +2408,51 @@ bool swift::isRedundantMoveValue(MoveValueInst *mvi) {
2405
2408
2406
2409
// The move doesn't alter constraints: ownership and lexicality match.
2407
2410
2411
+ // Before checking whether escaping matches, check whether the move_value is
2412
+ // redundant regardless on account of how its uses are limited.
2413
+ //
2414
+ // At this point, ownership and lexicality are known to match. If the
2415
+ // original value doesn't escape, then merging the two lifetimes won't make
2416
+ // it harder to optimize the portion of the merged lifetime corresponding to
2417
+ // the moved-to value. If the original's only consuming use is the
2418
+ // move_value, then the original value's lifetime couldn't be shortened
2419
+ // anyway.
2420
+ //
2421
+ // Summary: !escaping(original)
2422
+ // && singleConsumingUser(original) == move
2423
+ // => redundant(mvi)
2424
+ //
2425
+ // Check this in two ways, one cheaper than the other.
2426
+
2427
+ // First, avoid calling hasPointerEscape(original).
2428
+ //
2429
+ // If the original value is not a phi (a phi's incoming values might have
2430
+ // escaping uses) and its only user is the move, then it doesn't escape. Also
2431
+ // if its only user is the move, then its only _consuming_ user is the move.
2408
2432
auto *singleUser =
2409
2433
original->getSingleUse () ? original->getSingleUse ()->getUser () : nullptr ;
2410
2434
if (mvi == singleUser && !SILArgument::asPhi (original)) {
2411
2435
assert (!hasPointerEscape (original));
2412
- // The moved-from value's only use is the move_value, and the moved-from
2413
- // value isn't a phi. So, it doesn't escape. (A phi's incoming values
2414
- // might escape.)
2415
- //
2416
- // Still, no optimization is enabled by separating the two lifetimes.
2417
- // The moved-from value's lifetime could not be shrunk regardless of whether
2418
- // the moved-to value escapes.
2436
+ assert (original->getSingleConsumingUse ()->getUser () == mvi);
2437
+ // - !escaping(original)
2438
+ // - singleConsumingUser(original) == move
2419
2439
return true ;
2420
2440
}
2421
2441
2422
- auto moveHasEscape = hasPointerEscape (mvi);
2442
+ // Second, call hasPointerEscape(original).
2443
+ //
2444
+ // Explicitly check both
2445
+ // - !escaping(original)
2446
+ // - singleConsumingUser(original) == move
2423
2447
auto originalHasEscape = hasPointerEscape (original);
2424
-
2425
- // (3) Escaping matches? (Expensive check, saved for last.)
2426
- if (moveHasEscape != originalHasEscape) {
2427
- if (!originalHasEscape) {
2428
- auto *singleConsumingUser =
2429
- original->getSingleConsumingUse ()
2430
- ? original->getSingleConsumingUse ()->getUser ()
2431
- : nullptr ;
2432
- if (mvi == singleConsumingUser) {
2433
- // The moved-from value's only consuming use is the move_value and it
2434
- // doesn't escape.
2435
- //
2436
- // Although the moved-to value escapes, no optimization is enabled by
2437
- // separating the two lifetimes. The moved-from value's lifetime
2438
- // already couldn't be shrunk.
2439
- return true ;
2440
- }
2441
- }
2442
- return false ;
2448
+ auto *singleConsumingUser = original->getSingleConsumingUse ()
2449
+ ? original->getSingleConsumingUse ()->getUser ()
2450
+ : nullptr ;
2451
+ if (mvi == singleConsumingUser && !originalHasEscape) {
2452
+ return true ;
2443
2453
}
2444
2454
2445
- return true ;
2455
+ // (3) Escaping matches? (Expensive check, saved for last.)
2456
+ auto moveHasEscape = hasPointerEscape (mvi);
2457
+ return moveHasEscape == originalHasEscape;
2446
2458
}
0 commit comments