Skip to content

Commit 156f1fc

Browse files
committed
Optimizer: prevent duplicating blocks with dead-end begin-borrow instructions.
In case the control flow ends in a dead-end block there can be begin-borrow instructions which have no corresponding end-borrow uses. After duplicating such a block, the re-borrow flags cannot be recomputed correctly for inserted phi arguments. Therefore just disable duplicating such blocks, e.g. in jump-threading.
1 parent 51e3e5e commit 156f1fc

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,12 @@ struct BorrowedValue {
618618
/// BorrowScopeIntroducingValue::isLocalScope().
619619
bool visitLocalScopeEndingUses(function_ref<bool(Operand *)> visitor) const;
620620

621+
/// Returns false if the value has no scope-ending uses because all control flow
622+
/// paths end in dead-end blocks.
623+
bool hasLocalScopeEndingUses() const {
624+
return !visitLocalScopeEndingUses([](Operand *) { return false; });
625+
}
626+
621627
bool isLocalScope() const { return kind.isLocalScope(); }
622628

623629
/// Add this scope's live blocks into the PrunedLiveness result. This

lib/SIL/IR/SILInstruction.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/SIL/ApplySite.h"
2323
#include "swift/SIL/DynamicCasts.h"
2424
#include "swift/SIL/InstructionUtils.h"
25+
#include "swift/SIL/OwnershipUtils.h"
2526
#include "swift/SIL/SILBuilder.h"
2627
#include "swift/SIL/SILCloner.h"
2728
#include "swift/SIL/SILDebugScope.h"
@@ -1479,6 +1480,17 @@ bool SILInstruction::isTriviallyDuplicatable() const {
14791480
isa<GetAsyncContinuationInst>(this))
14801481
return false;
14811482

1483+
// Bail if there are any begin-borrow instructions which have no corresponding
1484+
// end-borrow uses. This is the case if the control flow ends in a dead-end block.
1485+
// After duplicating such a block, the re-borrow flags cannot be recomputed
1486+
// correctly for inserted phi arguments.
1487+
if (auto *svi = dyn_cast<SingleValueInstruction>(this)) {
1488+
if (auto bv = BorrowedValue(lookThroughBorrowedFromDef(svi))) {
1489+
if (!bv.hasLocalScopeEndingUses())
1490+
return false;
1491+
}
1492+
}
1493+
14821494
// If you add more cases here, you should also update SILLoop:canDuplicate.
14831495

14841496
return true;

lib/SILOptimizer/Utils/LoopUtils.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/SIL/BasicBlockUtils.h"
1818
#include "swift/SIL/Dominance.h"
1919
#include "swift/SIL/LoopInfo.h"
20+
#include "swift/SIL/OwnershipUtils.h"
2021
#include "swift/SIL/SILArgument.h"
2122
#include "swift/SIL/SILBasicBlock.h"
2223
#include "swift/SIL/SILBuilder.h"
@@ -331,6 +332,17 @@ bool swift::canDuplicateLoopInstruction(SILLoop *L, SILInstruction *I) {
331332
isa<GetAsyncContinuationAddrInst>(I) || isa<GetAsyncContinuationInst>(I))
332333
return false;
333334

335+
// Bail if there are any begin-borrow instructions which have no corresponding
336+
// end-borrow uses. This is the case if the control flow ends in a dead-end block.
337+
// After duplicating such a block, the re-borrow flags cannot be recomputed
338+
// correctly for inserted phi arguments.
339+
if (auto *svi = dyn_cast<SingleValueInstruction>(I)) {
340+
if (auto bv = BorrowedValue(lookThroughBorrowedFromDef(svi))) {
341+
if (!bv.hasLocalScopeEndingUses())
342+
return false;
343+
}
344+
}
345+
334346
// Some special cases above that aren't considered isTriviallyDuplicatable
335347
// return true early.
336348
assert(I->isTriviallyDuplicatable() &&

test/SILOptimizer/simplify_cfg_dom_jumpthread.sil

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,64 @@ bb6:
497497
%r = tuple ()
498498
return %r : $()
499499
}
500+
501+
// CHECK-LABEL: sil [ossa] @test_begin_borrow_in_duplicated_block :
502+
// CHECK: begin_borrow
503+
// CHECK: begin_borrow
504+
// CHECK: @reborrow
505+
// CHECK: } // end sil function 'test_begin_borrow_in_duplicated_block'
506+
sil [ossa] @test_begin_borrow_in_duplicated_block : $@convention(thin) (@guaranteed E2, @guaranteed E2) -> @owned E2 {
507+
bb0(%0 : @guaranteed $E2, %1 : @guaranteed $E2):
508+
switch_enum %0 : $E2, case #E2.b!enumelt: bb1, default bb2
509+
510+
bb1(%3 : @guaranteed $C):
511+
br bb3
512+
513+
bb2(%4 : @guaranteed $E2):
514+
br bb3
515+
516+
bb3:
517+
%5 = copy_value %1 : $E2
518+
%6 = begin_borrow %5 : $E2
519+
switch_enum %0 : $E2, case #E2.c!enumelt: bb4, default bb5
520+
521+
bb4(%8 : @guaranteed $C):
522+
br bb6
523+
524+
bb5(%11 : @guaranteed $E2):
525+
br bb6
526+
527+
bb6:
528+
fix_lifetime %6 : $E2
529+
end_borrow %6 : $E2
530+
return %5 : $E2
531+
}
532+
533+
// CHECK-LABEL: sil [ossa] @test_begin_borrow_and_unreachable :
534+
// CHECK-NOT: @reborrow
535+
// CHECK: } // end sil function 'test_begin_borrow_and_unreachable'
536+
sil [ossa] @test_begin_borrow_and_unreachable : $@convention(thin) (@guaranteed E2, @guaranteed E2) -> () {
537+
bb0(%0 : @guaranteed $E2, %1 : @guaranteed $E2):
538+
switch_enum %0 : $E2, case #E2.b!enumelt: bb1, default bb2
539+
540+
bb1(%3 : @guaranteed $C):
541+
br bb3
542+
543+
bb2(%4 : @guaranteed $E2):
544+
br bb3
545+
546+
bb3:
547+
%5 = copy_value %1 : $E2
548+
%6 = begin_borrow %5 : $E2
549+
switch_enum %0 : $E2, case #E2.c!enumelt: bb4, default bb5
550+
551+
bb4(%8 : @guaranteed $C):
552+
br bb6
553+
554+
bb5(%11 : @guaranteed $E2):
555+
br bb6
556+
557+
bb6:
558+
fix_lifetime %6 : $E2
559+
unreachable
560+
}

0 commit comments

Comments
 (0)