Skip to content

Commit 2984f0a

Browse files
Merge pull request #65417 from nate-chandler/rdar108514447
[SemanticARCOpts] Promote moved load [copy]s to load_borrows.
2 parents f0d76fa + f039d40 commit 2984f0a

File tree

6 files changed

+98
-8
lines changed

6 files changed

+98
-8
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,9 @@ class OwnedValueIntroducerKind {
11041104
/// memory location.
11051105
LoadTake,
11061106

1107+
/// An owned value produced by moving from another owned value.
1108+
Move,
1109+
11071110
/// An owned value that is a result of a true phi argument.
11081111
///
11091112
/// A true phi argument here is defined as an SIL phi argument that only has
@@ -1178,6 +1181,8 @@ class OwnedValueIntroducerKind {
11781181
return Kind::LoadCopy;
11791182
return Kind::Invalid;
11801183
}
1184+
case ValueKind::MoveValueInst:
1185+
return Kind::Move;
11811186
case ValueKind::PartialApplyInst:
11821187
return Kind::PartialApplyInit;
11831188
case ValueKind::AllocBoxInst:
@@ -1250,6 +1255,7 @@ struct OwnedValueIntroducer {
12501255
case OwnedValueIntroducerKind::BeginApply:
12511256
case OwnedValueIntroducerKind::TryApply:
12521257
case OwnedValueIntroducerKind::LoadTake:
1258+
case OwnedValueIntroducerKind::Move:
12531259
case OwnedValueIntroducerKind::Phi:
12541260
case OwnedValueIntroducerKind::Struct:
12551261
case OwnedValueIntroducerKind::Tuple:
@@ -1280,6 +1286,7 @@ struct OwnedValueIntroducer {
12801286
case OwnedValueIntroducerKind::BeginApply:
12811287
case OwnedValueIntroducerKind::TryApply:
12821288
case OwnedValueIntroducerKind::LoadTake:
1289+
case OwnedValueIntroducerKind::Move:
12831290
case OwnedValueIntroducerKind::FunctionArgument:
12841291
case OwnedValueIntroducerKind::PartialApplyInit:
12851292
case OwnedValueIntroducerKind::AllocBoxInit:

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,9 @@ void OwnedValueIntroducerKind::print(llvm::raw_ostream &os) const {
10731073
case OwnedValueIntroducerKind::LoadTake:
10741074
os << "LoadTake";
10751075
return;
1076+
case OwnedValueIntroducerKind::Move:
1077+
os << "Move";
1078+
return;
10761079
case OwnedValueIntroducerKind::Phi:
10771080
os << "Phi";
10781081
return;

lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,6 @@ static bool isWrittenTo(Context &ctx, LoadInst *load,
319319
// Top Level Entrypoint
320320
//===----------------------------------------------------------------------===//
321321

322-
// Convert a load [copy] from unique storage [read] that has all uses that can
323-
// accept a guaranteed parameter to a load_borrow.
324322
bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
325323
// This optimization can use more complex analysis. We should do some
326324
// experiments before enabling this by default as a guaranteed optimization.
@@ -334,14 +332,36 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
334332
if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
335333
return false;
336334

337-
// Ok, we have our load [copy]. Make sure its value is truly a dead live range
338-
// implying it is only ever consumed by destroy_value instructions. If it is
339-
// consumed, we need to pass off a +1 value, so bail.
335+
// Ok, we have our load [copy]. Try to optimize considering its live range.
336+
if (performLoadCopyToLoadBorrowOptimization(li, li))
337+
return true;
338+
// Check whether the load [copy]'s only use is as an operand to a move_value.
339+
auto *use = li->getSingleUse();
340+
if (!use)
341+
return false;
342+
auto *mvi = dyn_cast<MoveValueInst>(use->getUser());
343+
if (!mvi)
344+
return false;
345+
// Try to optimize considering the move_value's live range.
346+
return performLoadCopyToLoadBorrowOptimization(li, mvi);
347+
}
348+
349+
// Convert a load [copy] from unique storage [read] whose representative
350+
// (either the load [copy] itself or a move from it) has all uses that can
351+
// accept a guaranteed parameter to a load_borrow.
352+
bool SemanticARCOptVisitor::performLoadCopyToLoadBorrowOptimization(
353+
LoadInst *li, SILValue original) {
354+
assert(li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy);
355+
assert(li == original || li->getSingleUse()->getUser() ==
356+
cast<SingleValueInstruction>(original));
357+
// Make sure its value is truly a dead live range implying it is only ever
358+
// consumed by destroy_value instructions. If it is consumed, we need to pass
359+
// off a +1 value, so bail.
340360
//
341361
// FIXME: We should consider if it is worth promoting a load [copy]
342362
// -> load_borrow if we can put a copy_value on a cold path and thus
343363
// eliminate RR traffic on a hot path.
344-
OwnershipLiveRange lr(li);
364+
OwnershipLiveRange lr(original);
345365
if (bool(lr.hasUnknownConsumingUse()))
346366
return false;
347367

@@ -355,8 +375,17 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
355375
// load_borrow.
356376
auto *lbi =
357377
SILBuilderWithScope(li).createLoadBorrow(li->getLoc(), li->getOperand());
358-
359378
lr.insertEndBorrowsAtDestroys(lbi, getDeadEndBlocks(), ctx.lifetimeFrontier);
360-
std::move(lr).convertToGuaranteedAndRAUW(lbi, getCallbacks());
379+
SILValue replacement = lbi;
380+
if (original != li) {
381+
getCallbacks().eraseAndRAUWSingleValueInst(li, lbi);
382+
auto *bbi =
383+
SILBuilderWithScope(cast<SingleValueInstruction>(original))
384+
.createBeginBorrow(li->getLoc(), lbi, original->isLexical());
385+
replacement = bbi;
386+
lr.insertEndBorrowsAtDestroys(bbi, getDeadEndBlocks(),
387+
ctx.lifetimeFrontier);
388+
}
389+
std::move(lr).convertToGuaranteedAndRAUW(replacement, getCallbacks());
361390
return true;
362391
}

lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) {
296296
case OwnedValueIntroducerKind::BeginApply:
297297
case OwnedValueIntroducerKind::TryApply:
298298
case OwnedValueIntroducerKind::LoadTake:
299+
case OwnedValueIntroducerKind::Move:
299300
case OwnedValueIntroducerKind::FunctionArgument:
300301
case OwnedValueIntroducerKind::PartialApplyInit:
301302
case OwnedValueIntroducerKind::AllocBoxInit:

lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
202202
bool optimize();
203203
bool optimizeWithoutFixedPoint();
204204

205+
bool performLoadCopyToLoadBorrowOptimization(LoadInst *li, SILValue original);
205206
bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi);
206207
bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi);
207208
bool tryJoiningCopyValueLiveRangeWithOperand(CopyValueInst *cvi);

test/SILOptimizer/semantic-arc-opts-loadcopy-to-loadborrow.sil

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ final class Klass {
5454
let baseLet: Klass
5555
}
5656

57+
class C {}
58+
59+
sil @getC : $@convention(thin) () -> (@owned C)
60+
5761
extension Klass : MyFakeAnyObject {
5862
func myFakeMethod()
5963
}
@@ -1552,3 +1556,48 @@ bb2(%7 : @owned $NonTrivialStruct):
15521556
bb3:
15531557
unreachable
15541558
}
1559+
1560+
// CHECK-LABEL: sil [ossa] @promote_moved_from_load_copy : {{.*}} {
1561+
// CHECK: [[ADDR:%[^,]+]] = alloc_stack $C
1562+
// CHECK: [[GET_C:%[^,]+]] = function_ref @getC
1563+
// CHECK: [[STORED:%[^,]+]] = apply [[GET_C]]()
1564+
// CHECK: store [[STORED]] to [init] [[ADDR]]
1565+
// CHECK: [[LOADED:%[^,]+]] = load_borrow [[ADDR]]
1566+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[LOADED]]
1567+
// CHECK: end_borrow [[LIFETIME]]
1568+
// CHECK: end_borrow [[LOADED]]
1569+
// CHECK: destroy_addr [[ADDR]]
1570+
// CHECK: dealloc_stack [[ADDR]]
1571+
// CHECK-LABEL: } // end sil function 'promote_moved_from_load_copy'
1572+
sil [ossa] @promote_moved_from_load_copy : $@convention(thin) () -> () {
1573+
bb0:
1574+
%addr = alloc_stack $C
1575+
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
1576+
%stored = apply %getC() : $@convention(thin) () -> (@owned C)
1577+
store %stored to [init] %addr : $*C
1578+
%loaded = load [copy] %addr : $*C
1579+
%lifetime = move_value [lexical] %loaded : $C
1580+
destroy_value %lifetime : $C
1581+
destroy_addr %addr : $*C
1582+
dealloc_stack %addr : $*C
1583+
%retval = tuple ()
1584+
return %retval : $()
1585+
}
1586+
1587+
// CHECK-LABEL: sil [ossa] @dont_promote_moved_from_load_copy_with_destroy_addr_above_range : {{.*}} {
1588+
// CHECK: load [copy]
1589+
// CHECK-LABEL: } // end sil function 'dont_promote_moved_from_load_copy_with_destroy_addr_above_range'
1590+
sil [ossa] @dont_promote_moved_from_load_copy_with_destroy_addr_above_range : $@convention(thin) () -> () {
1591+
bb0:
1592+
%addr = alloc_stack $C
1593+
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
1594+
%stored = apply %getC() : $@convention(thin) () -> (@owned C)
1595+
store %stored to [init] %addr : $*C
1596+
%loaded = load [copy] %addr : $*C
1597+
destroy_addr %addr : $*C
1598+
%lifetime = move_value [lexical] %loaded : $C
1599+
destroy_value %lifetime : $C
1600+
dealloc_stack %addr : $*C
1601+
%retval = tuple ()
1602+
return %retval : $()
1603+
}

0 commit comments

Comments
 (0)