@@ -2405,42 +2405,52 @@ bool swift::isRedundantMoveValue(MoveValueInst *mvi) {
2405
2405
2406
2406
// The move doesn't alter constraints: ownership and lexicality match.
2407
2407
2408
+ // Before checking whether escaping matches, check whether the move_value is
2409
+ // redundant regardless on account of how its uses are limited.
2410
+ //
2411
+ // At this point, ownership and lexicality are known to match. If the
2412
+ // original value doesn't escape, then merging the two lifetimes won't make
2413
+ // it harder to optimize the portion of the merged lifetime corresponding to
2414
+ // the moved-to value. If the original's only consuming use is the
2415
+ // move_value, then the moved-from value's lifetime couldn't be shortened
2416
+ // anyway.
2417
+ //
2418
+ // Summary: !escaping(original)
2419
+ // && singleConsumingUser(original) == move
2420
+ // => redundant(mvi)
2421
+ //
2422
+ // Check this in two ways, one cheaper than the other.
2423
+
2424
+ // First, avoid calling hasPointerEscape(original).
2425
+ //
2426
+ // If the moved-from value is not a phi (a phi's incoming values might have
2427
+ // escaping uses) and its only user is the move, then it doesn't escape.
2428
+ // Also if its only user is the move, then its only _consuming_ user is the
2429
+ // move.
2408
2430
auto *singleUser =
2409
2431
original->getSingleUse () ? original->getSingleUse ()->getUser () : nullptr ;
2410
2432
if (mvi == singleUser && !SILArgument::asPhi (original)) {
2411
2433
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.
2434
+ assert (original->getSingleConsumingUse ()->getUser () == mvi);
2435
+ // - !escaping(original)
2436
+ // - singleConsumingUser(original) == move
2419
2437
return true ;
2420
2438
}
2421
2439
2422
- auto moveHasEscape = hasPointerEscape (mvi);
2440
+ // Second, call hasPointerEscape(original).
2441
+ //
2442
+ // Explicitly check both
2443
+ // - !escaping(original)
2444
+ // - singleConsumingUser(original) == move
2423
2445
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 ;
2446
+ auto *singleConsumingUser = original->getSingleConsumingUse ()
2447
+ ? original->getSingleConsumingUse ()->getUser ()
2448
+ : nullptr ;
2449
+ if (mvi == singleConsumingUser && !originalHasEscape) {
2450
+ return true ;
2443
2451
}
2444
2452
2445
- return true ;
2453
+ // (3) Escaping matches? (Expensive check, saved for last.)
2454
+ auto moveHasEscape = hasPointerEscape (mvi);
2455
+ return moveHasEscape == originalHasEscape;
2446
2456
}
0 commit comments