Skip to content

Commit f039d40

Browse files
committed
[SemanticARCOpts] Load_borrow moved load [copy]s.
In the context of promoting a `load [copy]` to a `load_borrow`, allow the value representing the lifetime of the load to differ from the `load [copy]` itself--it may either be the load or its only user. If promoting the `load [copy]` to a `load_borrow` with the load itself as the lifetime representative fails, look for a single `move_value` use of the load. If found, attempt the optimization again with the `move_value` as the lifetime representative. rdar://108514447
1 parent 8287d2e commit f039d40

File tree

3 files changed

+77
-7
lines changed

3 files changed

+77
-7
lines changed

lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -332,24 +332,36 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
332332
if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
333333
return false;
334334

335-
// Ok, we have our load [copy]. Try to optimize.
336-
return performLoadCopyToLoadBorrowOptimization(li);
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);
337347
}
338348

339-
// Convert a load [copy] from unique storage [read] that has all uses that can
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
340351
// accept a guaranteed parameter to a load_borrow.
341352
bool SemanticARCOptVisitor::performLoadCopyToLoadBorrowOptimization(
342-
LoadInst *li) {
353+
LoadInst *li, SILValue original) {
343354
assert(li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy);
344-
355+
assert(li == original || li->getSingleUse()->getUser() ==
356+
cast<SingleValueInstruction>(original));
345357
// Make sure its value is truly a dead live range implying it is only ever
346358
// consumed by destroy_value instructions. If it is consumed, we need to pass
347359
// off a +1 value, so bail.
348360
//
349361
// FIXME: We should consider if it is worth promoting a load [copy]
350362
// -> load_borrow if we can put a copy_value on a cold path and thus
351363
// eliminate RR traffic on a hot path.
352-
OwnershipLiveRange lr(li);
364+
OwnershipLiveRange lr(original);
353365
if (bool(lr.hasUnknownConsumingUse()))
354366
return false;
355367

@@ -365,6 +377,15 @@ bool SemanticARCOptVisitor::performLoadCopyToLoadBorrowOptimization(
365377
SILBuilderWithScope(li).createLoadBorrow(li->getLoc(), li->getOperand());
366378
lr.insertEndBorrowsAtDestroys(lbi, getDeadEndBlocks(), ctx.lifetimeFrontier);
367379
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+
}
368389
std::move(lr).convertToGuaranteedAndRAUW(replacement, getCallbacks());
369390
return true;
370391
}

lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h

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

205-
bool performLoadCopyToLoadBorrowOptimization(LoadInst *li);
205+
bool performLoadCopyToLoadBorrowOptimization(LoadInst *li, SILValue original);
206206
bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi);
207207
bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi);
208208
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)