Skip to content

Commit 10e86d6

Browse files
committed
[CanonicalizeBorrowScope] Look through moves.
When encountering inside a borrow scope a non-lexical move_value or a move_value [lexical] where the borrowed value is itself already lexical, delete the move_value and regard its uses as uses of the moved-from value.
1 parent 11b76da commit 10e86d6

File tree

5 files changed

+117
-24
lines changed

5 files changed

+117
-24
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109

110110
namespace swift {
111111

112-
extern llvm::Statistic NumCopiesEliminated;
112+
extern llvm::Statistic NumCopiesAndMovesEliminated;
113113
extern llvm::Statistic NumCopiesGenerated;
114114

115115
/// Insert a copy on this operand. Trace and update stats.

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,23 @@ struct CanonicalizeOSSALifetimeTest : UnitTest {
393393
}
394394
};
395395

396+
// Arguments:
397+
// - SILValue: value to canonicalize
398+
// Dumps:
399+
// - function after value canonicalization
400+
struct CanonicalizeBorrowScopeTest : UnitTest {
401+
CanonicalizeBorrowScopeTest(UnitTestRunner *pass) : UnitTest(pass) {}
402+
void invoke(Arguments &arguments) override {
403+
auto value = arguments.takeValue();
404+
auto borrowedValue = BorrowedValue(value);
405+
assert(borrowedValue && "specified value isn't a BorrowedValue!?");
406+
InstructionDeleter deleter;
407+
CanonicalizeBorrowScope canonicalizer(deleter);
408+
canonicalizer.canonicalizeBorrowScope(borrowedValue);
409+
getFunction()->dump();
410+
}
411+
};
412+
396413
// Arguments:
397414
// - instruction
398415
// Dumps:
@@ -616,6 +633,8 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) {
616633

617634
// Alphabetical mapping from string to unit test subclass.
618635
ADD_UNIT_TEST_SUBCLASS("canonicalize-ossa-lifetime", CanonicalizeOSSALifetimeTest)
636+
ADD_UNIT_TEST_SUBCLASS("canonicalize-borrow-scope",
637+
CanonicalizeBorrowScopeTest)
619638
ADD_UNIT_TEST_SUBCLASS("dump-function", DumpFunction)
620639
ADD_UNIT_TEST_SUBCLASS("find-borrow-introducers", FindBorrowIntroducers)
621640
ADD_UNIT_TEST_SUBCLASS("find-enclosing-defs", FindEnclosingDefsTest)

lib/SILOptimizer/Utils/CanonicalizeBorrowScope.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,24 @@ static bool hasValueOwnership(SILValue value) {
4444
value->getOwnershipKind() == OwnershipKind::Owned;
4545
}
4646

47-
/// Delete a chain of unused copies leading to \p v.
48-
static void deleteCopyChain(SILValue v, InstructionDeleter &deleter) {
49-
while (auto *copy = dyn_cast<CopyValueInst>(v)) {
50-
if (!onlyHaveDebugUses(copy))
47+
static SingleValueInstruction *asCopyOrMove(SILValue v) {
48+
if (auto *copy = dyn_cast<CopyValueInst>(v))
49+
return copy;
50+
if (auto *move = dyn_cast<MoveValueInst>(v))
51+
return move;
52+
return nullptr;
53+
}
54+
55+
/// Delete a chain of unused copies and moves leading to \p v.
56+
static void deleteCopyAndMoveChain(SILValue v, InstructionDeleter &deleter) {
57+
while (auto *inst = asCopyOrMove(v)) {
58+
if (!onlyHaveDebugUses(inst))
5159
break;
5260

53-
v = copy->getOperand();
54-
LLVM_DEBUG(llvm::dbgs() << " Deleting " << *copy);
55-
++NumCopiesEliminated;
56-
deleter.forceDelete(copy);
61+
v = inst->getOperand(CopyLikeInstruction::Src);
62+
LLVM_DEBUG(llvm::dbgs() << " Deleting " << *inst);
63+
++NumCopiesAndMovesEliminated;
64+
deleter.forceDelete(inst);
5765
}
5866
}
5967

@@ -182,11 +190,12 @@ bool CanonicalizeBorrowScope::computeBorrowLiveness() {
182190
/// equivalent to the logic in visitBorrowScopeUses that recurses through
183191
/// copies. The use-def and def-use logic must be consistent.
184192
SILValue CanonicalizeBorrowScope::findDefInBorrowScope(SILValue value) {
185-
while (auto *copy = dyn_cast<CopyValueInst>(value)) {
186-
if (isPersistentCopy(copy))
193+
while (auto *inst = asCopyOrMove(value)) {
194+
auto *copy = dyn_cast<CopyValueInst>(inst);
195+
if (copy && isPersistentCopy(copy))
187196
return copy;
188197

189-
value = copy->getOperand();
198+
value = inst->getOperand(0);
190199
}
191200
return value;
192201
}
@@ -224,21 +233,27 @@ bool CanonicalizeBorrowScope::visitBorrowScopeUses(SILValue innerValue,
224233
// Gather the uses before updating any of them.
225234
// 'value' may be deleted in this loop after rewriting its last use.
226235
// 'use' may become invalid after processing its user.
236+
227237
SmallVector<Operand *, 4> uses(value->getUses());
228-
for (Operand *use : uses) {
238+
for (auto *use : uses) {
229239
auto *user = use->getUser();
230240
// Incidental uses, such as debug_value may be deleted before they can be
231241
// processed. Their user will now be nullptr. This means that value
232242
// is dead, so just bail.
233243
if (!user)
234244
break;
235245

236-
// Recurse through copies.
246+
// Recurse through copies and moves.
237247
if (auto *copy = dyn_cast<CopyValueInst>(user)) {
238248
if (!isPersistentCopy(copy)) {
239249
defUseWorklist.insert(copy);
240250
continue;
241251
}
252+
} else if (auto *move = dyn_cast<MoveValueInst>(user)) {
253+
if (!move->isLexical() || innerValue->isLexical()) {
254+
defUseWorklist.insert(move);
255+
continue;
256+
}
242257
}
243258
// Note: debug_value uses are handled like normal uses here. If they are
244259
// inner uses, they remain. If they are outer uses, then they will be
@@ -433,7 +448,7 @@ class RewriteInnerBorrowUses {
433448
// destroys are never needed within a borrow scope.
434449
if (isa<DestroyValueInst>(user)) {
435450
scope.getDeleter().forceDelete(user);
436-
deleteCopyChain(value, scope.getDeleter());
451+
deleteCopyAndMoveChain(value, scope.getDeleter());
437452
return true;
438453
}
439454
SILValue def = scope.findDefInBorrowScope(value);
@@ -447,12 +462,12 @@ class RewriteInnerBorrowUses {
447462
use->set(def);
448463
copyLiveUse(use, scope.getCallbacks());
449464
}
450-
deleteCopyChain(value, scope.getDeleter());
465+
deleteCopyAndMoveChain(value, scope.getDeleter());
451466
return true;
452467
}
453468
// Non-consuming use.
454469
use->set(def);
455-
deleteCopyChain(value, scope.getDeleter());
470+
deleteCopyAndMoveChain(value, scope.getDeleter());
456471
return true;
457472
}
458473

@@ -474,7 +489,7 @@ class RewriteInnerBorrowUses {
474489
use->set(scope.findDefInBorrowScope(value));
475490
ForwardingOperand(use).setForwardingOwnershipKind(
476491
OwnershipKind::Guaranteed);
477-
deleteCopyChain(value, scope.getDeleter());
492+
deleteCopyAndMoveChain(value, scope.getDeleter());
478493
return true;
479494
}
480495
};
@@ -593,7 +608,7 @@ class RewriteOuterBorrowUses {
593608
ForwardingOperand(use).setForwardingOwnershipKind(
594609
OwnershipKind::Guaranteed);
595610
}
596-
deleteCopyChain(innerValue, scope.getDeleter());
611+
deleteCopyAndMoveChain(innerValue, scope.getDeleter());
597612
return true;
598613
}
599614

@@ -644,7 +659,7 @@ class RewriteOuterBorrowUses {
644659

645660
use->set(outerValue);
646661

647-
deleteCopyChain(innerValue, scope.getDeleter());
662+
deleteCopyAndMoveChain(innerValue, scope.getDeleter());
648663

649664
recordOuterUse(use);
650665
};

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@
7878
using namespace swift;
7979
using llvm::SmallSetVector;
8080

81-
llvm::Statistic swift::NumCopiesEliminated = {
82-
DEBUG_TYPE, "NumCopiesEliminated",
83-
"number of copy_value instructions removed"};
81+
llvm::Statistic swift::NumCopiesAndMovesEliminated = {
82+
DEBUG_TYPE, "NumCopiesAndMovesEliminated",
83+
"number of copy_value and move_value instructions removed"};
8484

8585
llvm::Statistic swift::NumCopiesGenerated = {
8686
DEBUG_TYPE, "NumCopiesGenerated",
@@ -970,7 +970,7 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
970970
} else {
971971
if (instsToDelete.insert(srcCopy)) {
972972
LLVM_DEBUG(llvm::dbgs() << " Removing " << *srcCopy);
973-
++NumCopiesEliminated;
973+
++NumCopiesAndMovesEliminated;
974974
}
975975
}
976976
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s
2+
3+
import Builtin
4+
5+
typealias AnyObject = Builtin.AnyObject
6+
7+
struct Unmanaged<Instance> where Instance : AnyObject {
8+
unowned(unsafe) var _value: @sil_unmanaged Instance
9+
}
10+
11+
// CHECK-LABEL: begin {{.*}} on copy_and_move_argument: canonicalize-borrow-scope
12+
// CHECK-LABEL: sil [ossa] @copy_and_move_argument : {{.*}} {
13+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] :
14+
// CHECK: [[UNMANAGED:%[^,]+]] = ref_to_unmanaged [[INSTANCE]]
15+
// CHECK: [[RETVAL:%[^,]+]] = struct $Unmanaged<Instance> ([[UNMANAGED]] : $@sil_unmanaged Instance)
16+
// CHECK: return [[RETVAL]]
17+
// CHECK-LABEL: } // end sil function 'copy_and_move_argument'
18+
// CHECK-LABEL: end {{.*}} on copy_and_move_argument: canonicalize-borrow-scope
19+
sil [ossa] @copy_and_move_argument : $@convention(thin) <Instance where Instance : AnyObject> (@guaranteed Instance) -> Unmanaged<Instance> {
20+
bb0(%instance : @guaranteed $Instance):
21+
test_specification "canonicalize-borrow-scope @argument"
22+
%copy_1 = copy_value %instance : $Instance
23+
%copy_2 = copy_value %copy_1 : $Instance
24+
%move = move_value %copy_2 : $Instance
25+
%copy_3 = copy_value %move : $Instance
26+
%copy_4 = copy_value %copy_3 : $Instance
27+
%unmanaged = ref_to_unmanaged %copy_4 : $Instance to $@sil_unmanaged Instance
28+
destroy_value %copy_4 : $Instance
29+
destroy_value %copy_3 : $Instance
30+
destroy_value %move : $Instance
31+
destroy_value %copy_1 : $Instance
32+
%retval = struct $Unmanaged<Instance> (%unmanaged : $@sil_unmanaged Instance)
33+
return %retval : $Unmanaged<Instance>
34+
}
35+
36+
// CHECK-LABEL: begin {{.*}} on copy_and_move_lexical_argument: canonicalize-borrow-scope
37+
// CHECK-LABEL: sil [ossa] @copy_and_move_lexical_argument : {{.*}} {
38+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] :
39+
// CHECK: [[UNMANAGED:%[^,]+]] = ref_to_unmanaged [[INSTANCE]]
40+
// CHECK: [[RETVAL:%[^,]+]] = struct $Unmanaged<Instance> ([[UNMANAGED]] : $@sil_unmanaged Instance)
41+
// CHECK: return [[RETVAL]]
42+
// CHECK-LABEL: } // end sil function 'copy_and_move_lexical_argument'
43+
// CHECK-LABEL: end {{.*}} on copy_and_move_lexical_argument: canonicalize-borrow-scope
44+
sil [ossa] @copy_and_move_lexical_argument : $@convention(thin) <Instance where Instance : AnyObject> (@guaranteed Instance) -> Unmanaged<Instance> {
45+
bb0(%instance : @guaranteed $Instance):
46+
test_specification "canonicalize-borrow-scope @argument"
47+
%copy_1 = copy_value %instance : $Instance
48+
%copy_2 = copy_value %copy_1 : $Instance
49+
%move = move_value [lexical] %copy_2 : $Instance
50+
%copy_3 = copy_value %move : $Instance
51+
%copy_4 = copy_value %copy_3 : $Instance
52+
%unmanaged = ref_to_unmanaged %copy_4 : $Instance to $@sil_unmanaged Instance
53+
destroy_value %copy_4 : $Instance
54+
destroy_value %copy_3 : $Instance
55+
destroy_value %move : $Instance
56+
destroy_value %copy_1 : $Instance
57+
%retval = struct $Unmanaged<Instance> (%unmanaged : $@sil_unmanaged Instance)
58+
return %retval : $Unmanaged<Instance>
59+
}

0 commit comments

Comments
 (0)