Skip to content

Commit 1f3623d

Browse files
Merge pull request #64339 from nate-chandler/copy-propagation/redundant-move-elim
[CopyPropagation] Eliminate redundant moves.
2 parents 5964bf2 + 1621692 commit 1f3623d

File tree

7 files changed

+139
-63
lines changed

7 files changed

+139
-63
lines changed

include/swift/SIL/OwnershipUtils.h

+8
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,14 @@ void visitTransitiveEndBorrows(
13471347
/// - the value is itself a begin_borrow [lexical]
13481348
bool isNestedLexicalBeginBorrow(BeginBorrowInst *bbi);
13491349

1350+
/// Whether specified move_value is redundant.
1351+
///
1352+
/// A move_value is redundant if the lifetimes that it separates both have the
1353+
/// same characteristics with respect to
1354+
/// - lexicality
1355+
/// - escaping
1356+
bool isRedundantMoveValue(MoveValueInst *mvi);
1357+
13501358
} // namespace swift
13511359

13521360
#endif

lib/SIL/Utils/OwnershipUtils.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -2381,3 +2381,25 @@ bool swift::isNestedLexicalBeginBorrow(BeginBorrowInst *bbi) {
23812381
return false;
23822382
});
23832383
}
2384+
2385+
bool swift::isRedundantMoveValue(MoveValueInst *mvi) {
2386+
auto original = mvi->getOperand();
2387+
2388+
// If the moved-from value has none ownership, hasPointerEscape can't handle
2389+
// it, so it can't be used to determine whether escaping matches.
2390+
if (original->getOwnershipKind() != OwnershipKind::Owned) {
2391+
return false;
2392+
}
2393+
2394+
// First, check whether lexicality matches, the cheaper check.
2395+
if (mvi->isLexical() != original->isLexical()) {
2396+
return false;
2397+
}
2398+
2399+
// Then, check whether escaping matches, the more expensive check.
2400+
if (hasPointerEscape(mvi) != hasPointerEscape(original)) {
2401+
return false;
2402+
}
2403+
2404+
return true;
2405+
}

lib/SILOptimizer/SemanticARC/RedundantMoveValueElimination.cpp

+2-17
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,10 @@ bool SemanticARCOptVisitor::visitMoveValueInst(MoveValueInst *mvi) {
3939
if (!ctx.shouldPerform(ARCTransformKind::RedundantMoveValueElim))
4040
return false;
4141

42-
auto original = mvi->getOperand();
43-
44-
// If the moved-from value has none ownership, hasPointerEscape can't handle
45-
// it, so it can't be used to determine whether escaping matches.
46-
if (original->getOwnershipKind() != OwnershipKind::Owned) {
47-
return false;
48-
}
49-
50-
// First, check whether lexicality matches, the cheaper check.
51-
if (mvi->isLexical() != original->isLexical()) {
52-
return false;
53-
}
54-
55-
// Then, check whether escaping matches, the more expensive check.
56-
if (hasPointerEscape(mvi) != hasPointerEscape(original)) {
42+
if (!isRedundantMoveValue(mvi))
5743
return false;
58-
}
5944

6045
// Both characteristics match.
61-
eraseAndRAUWSingleValueInstruction(mvi, original);
46+
eraseAndRAUWSingleValueInstruction(mvi, mvi->getOperand());
6247
return true;
6348
}

lib/SILOptimizer/Transforms/CopyPropagation.cpp

+29-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "swift/SIL/BasicBlockDatastructures.h"
4343
#include "swift/SIL/BasicBlockUtils.h"
4444
#include "swift/SIL/DebugUtils.h"
45+
#include "swift/SIL/OwnershipUtils.h"
4546
#include "swift/SIL/SILUndef.h"
4647
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
4748
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
@@ -263,6 +264,24 @@ static bool convertExtractsToDestructures(CanonicalDefWorklist &copiedDefs,
263264
return changed;
264265
}
265266

267+
//===----------------------------------------------------------------------===//
268+
// MARK: Eliminate redundant moves
269+
//===----------------------------------------------------------------------===//
270+
271+
/// If the specified move_value is redundant (there's no benefit to separating
272+
/// the lifetime at it), replace its uses with uses of the moved-from value and
273+
/// delete it.
274+
static bool eliminateRedundantMove(MoveValueInst *mvi,
275+
InstructionDeleter &deleter) {
276+
if (!isRedundantMoveValue(mvi))
277+
return false;
278+
mvi->replaceAllUsesWith(mvi->getOperand());
279+
// Call InstructionDeleter::forceDeleteWithUsers to avoid "fixing up"
280+
// ownership of the moved-from value, i.e. inserting a destroy_value.
281+
deleter.forceDeleteWithUsers(mvi);
282+
return true;
283+
}
284+
266285
//===----------------------------------------------------------------------===//
267286
// MARK: Sink owned forwarding operations
268287
//===----------------------------------------------------------------------===//
@@ -419,15 +438,18 @@ void CopyPropagation::run() {
419438
InstructionDeleter deleter(std::move(callbacks));
420439
bool changed = false;
421440

422-
GraphNodeWorklist<BeginBorrowInst *, 16> beginBorrowsToShrink;
441+
StackList<BeginBorrowInst *> beginBorrowsToShrink(f);
442+
StackList<MoveValueInst *> moveValues(f);
423443

424444
// Driver: Find all copied or borrowed defs.
425445
for (auto &bb : *f) {
426446
for (auto &i : bb) {
427447
if (auto *copy = dyn_cast<CopyValueInst>(&i)) {
428448
defWorklist.updateForCopy(copy);
429449
} else if (auto *borrow = dyn_cast<BeginBorrowInst>(&i)) {
430-
beginBorrowsToShrink.insert(borrow);
450+
beginBorrowsToShrink.push_back(borrow);
451+
} else if (auto *move = dyn_cast<MoveValueInst>(&i)) {
452+
moveValues.push_back(move);
431453
} else if (canonicalizeAll) {
432454
if (auto *destroy = dyn_cast<DestroyValueInst>(&i)) {
433455
defWorklist.updateForCopy(destroy->getOperand());
@@ -446,7 +468,7 @@ void CopyPropagation::run() {
446468
// NOTE: We assume that the function is in reverse post order so visiting the
447469
// blocks and pushing begin_borrows as we see them and then popping them
448470
// off the end will result in shrinking inner borrow scopes first.
449-
while (auto *bbi = beginBorrowsToShrink.pop()) {
471+
for (auto *bbi : beginBorrowsToShrink) {
450472
bool firstRun = true;
451473
// Run the sequence of utilities:
452474
// - ShrinkBorrowScope
@@ -480,9 +502,13 @@ void CopyPropagation::run() {
480502
hoistDestroysOfOwnedLexicalValue(folded, *f, deleter, calleeAnalysis);
481503
// Keep running even if the new move's destroys can't be hoisted.
482504
(void)hoisted;
505+
eliminateRedundantMove(folded, deleter);
483506
firstRun = false;
484507
}
485508
}
509+
for (auto *mvi : moveValues) {
510+
eliminateRedundantMove(mvi, deleter);
511+
}
486512
for (auto *argument : f->getArguments()) {
487513
if (argument->getOwnershipKind() == OwnershipKind::Owned) {
488514
hoistDestroysOfOwnedLexicalValue(argument, *f, deleter, calleeAnalysis);

test/SILOptimizer/copy_propagation.sil

+6-3
Original file line numberDiff line numberDiff line change
@@ -851,16 +851,19 @@ bb0(%0 : @owned $C):
851851
// Test that copy propagation doesn't hoist a destroy_value corresponding to
852852
// a move value [lexical] over a barrier.
853853
// CHECK-LABEL: sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : {{.*}} {
854-
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C):
854+
// CHECK: [[GET_OWNED_C:%.*]] = function_ref @getOwnedC
855+
// CHECK: [[INSTANCE:%.*]] = apply [[GET_OWNED_C]]
855856
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
856857
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
857858
// CHECK: [[TAKE_GUARANTEED_C:%[^,]+]] = function_ref @takeGuaranteedC
858859
// CHECK: apply [[TAKE_GUARANTEED_C]]([[LIFETIME]])
859860
// CHECK: apply [[BARRIER]]()
860861
// CHECK: destroy_value [[LIFETIME]]
861862
// CHECK-LABEL: } // end sil function 'dont_hoist_move_value_lexical_destroy_over_barrier_apply'
862-
sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : $@convention(thin) (@owned C) -> () {
863-
entry(%instance : @owned $C):
863+
sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : $@convention(thin) () -> () {
864+
entry:
865+
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
866+
%instance = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
864867
%lifetime = move_value [lexical] %instance : $C
865868
%barrier = function_ref @barrier : $@convention(thin) () -> ()
866869
%takeGuaranteedC = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()

0 commit comments

Comments
 (0)