Skip to content

Commit dc043cf

Browse files
committed
SILGen: Track original variable's cleanup state for addressable buffer cleanups.
Don't try to delete the value on paths where the value hasn't yet been deleted.
1 parent bcca7ee commit dc043cf

File tree

4 files changed

+72
-19
lines changed

4 files changed

+72
-19
lines changed

lib/SILGen/Cleanup.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ enum class CleanupState {
7474
PersistentlyActive
7575
};
7676

77+
inline bool isActiveCleanupState(CleanupState state) {
78+
return state >= CleanupState::Active;
79+
}
80+
7781
llvm::raw_ostream &operator<<(raw_ostream &os, CleanupState state);
7882

7983
class LLVM_LIBRARY_VISIBILITY Cleanup {
@@ -109,7 +113,7 @@ class LLVM_LIBRARY_VISIBILITY Cleanup {
109113
virtual void setState(SILGenFunction &SGF, CleanupState newState) {
110114
state = newState;
111115
}
112-
bool isActive() const { return state >= CleanupState::Active; }
116+
bool isActive() const { return isActiveCleanupState(state); }
113117
bool isDead() const { return state == CleanupState::Dead; }
114118

115119
virtual void emit(SILGenFunction &SGF, CleanupLocation loc,

lib/SILGen/SILGenDecl.cpp

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "Cleanup.h"
1314
#include "Initialization.h"
1415
#include "LValue.h"
1516
#include "RValue.h"
@@ -682,10 +683,18 @@ namespace {
682683

683684
static void deallocateAddressable(SILGenFunction &SGF,
684685
SILLocation l,
685-
const SILGenFunction::AddressableBuffer::State &state) {
686-
SGF.B.createEndBorrow(l, state.storeBorrow);
686+
const SILGenFunction::AddressableBuffer::State &state,
687+
CleanupState cleanupState) {
688+
// Only delete the value if the variable was active on this path.
689+
// The stack slot exists within the variable scope regardless, so always
690+
// needs to be cleaned up.
691+
bool active = isActiveCleanupState(cleanupState);
692+
if (active) {
693+
SGF.B.createEndBorrow(l, state.storeBorrow);
694+
}
687695
SGF.B.createDeallocStack(l, state.allocStack);
688-
if (state.reabstraction
696+
if (active
697+
&& state.reabstraction
689698
&& !state.reabstraction->getType().isTrivial(SGF.F)) {
690699
SGF.B.createDestroyValue(l, state.reabstraction);
691700
}
@@ -695,8 +704,11 @@ static void deallocateAddressable(SILGenFunction &SGF,
695704
/// binding.
696705
class DeallocateLocalVariableAddressableBuffer : public Cleanup {
697706
ValueDecl *vd;
707+
CleanupHandle destroyVarHandle;
698708
public:
699-
DeallocateLocalVariableAddressableBuffer(ValueDecl *vd) : vd(vd) {}
709+
DeallocateLocalVariableAddressableBuffer(ValueDecl *vd,
710+
CleanupHandle destroyVarHandle)
711+
: vd(vd), destroyVarHandle(destroyVarHandle) {}
700712

701713
void emit(SILGenFunction &SGF, CleanupLocation l,
702714
ForUnwind_t forUnwind) override {
@@ -705,14 +717,20 @@ class DeallocateLocalVariableAddressableBuffer : public Cleanup {
705717
return;
706718
}
707719

720+
// Reflect the state of the variable's original cleanup.
721+
CleanupState cleanupState = CleanupState::Active;
722+
if (destroyVarHandle.isValid()) {
723+
cleanupState = SGF.Cleanups.getCleanup(destroyVarHandle).getState();
724+
}
725+
708726
if (auto *state = addressableBuffer->getState()) {
709-
// The addressable buffer was forced, so clean it up now.
710-
deallocateAddressable(SGF, l, *state);
727+
// The addressable buffer was already forced, so clean up now.
728+
deallocateAddressable(SGF, l, *state, cleanupState);
711729
} else {
712730
// Remember this insert location in case we need to force the addressable
713731
// buffer later.
714732
SILInstruction *marker = SGF.B.createTuple(l, {});
715-
addressableBuffer->cleanupPoints.emplace_back(marker);
733+
addressableBuffer->cleanupPoints.push_back({marker, cleanupState});
716734
}
717735
}
718736

@@ -820,7 +838,7 @@ class LetValueInitialization : public Initialization {
820838

821839
// If the binding has an addressable buffer forced, it should be cleaned
822840
// up at this scope.
823-
SGF.enterLocalVariableAddressableBufferScope(vd);
841+
SGF.enterLocalVariableAddressableBufferScope(vd, DestroyCleanup);
824842
}
825843

826844
~LetValueInitialization() override {
@@ -2237,10 +2255,11 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
22372255
}
22382256

22392257
void
2240-
SILGenFunction::enterLocalVariableAddressableBufferScope(VarDecl *decl) {
2258+
SILGenFunction::enterLocalVariableAddressableBufferScope(VarDecl *decl,
2259+
CleanupHandle destroyCleanup) {
22412260
auto marker = B.createTuple(decl, {});
22422261
AddressableBuffers[decl].insertPoint = marker;
2243-
Cleanups.pushCleanup<DeallocateLocalVariableAddressableBuffer>(decl);
2262+
Cleanups.pushCleanup<DeallocateLocalVariableAddressableBuffer>(decl, destroyCleanup);
22442263
}
22452264

22462265
SILValue
@@ -2355,10 +2374,11 @@ SILGenFunction::getLocalVariableAddressableBuffer(VarDecl *decl,
23552374
decltype(addressableBuffer->cleanupPoints) cleanupPoints;
23562375
cleanupPoints.swap(addressableBuffer->cleanupPoints);
23572376

2358-
for (SILInstruction *cleanupPoint : cleanupPoints) {
2359-
SavedInsertionPointRAII insertCleanup(B, cleanupPoint);
2360-
deallocateAddressable(*this, cleanupPoint->getLoc(), *newState);
2361-
cleanupPoint->eraseFromParent();
2377+
for (auto &cleanupPoint : cleanupPoints) {
2378+
SavedInsertionPointRAII insertCleanup(B, cleanupPoint.inst);
2379+
deallocateAddressable(*this, cleanupPoint.inst->getLoc(), *newState,
2380+
cleanupPoint.state);
2381+
cleanupPoint.inst->eraseFromParent();
23622382
}
23632383

23642384
return storeBorrow;
@@ -2400,8 +2420,8 @@ SILGenFunction::AddressableBuffer::~AddressableBuffer() {
24002420
if (insertPoint) {
24012421
insertPoint->eraseFromParent();
24022422
}
2403-
for (auto cleanupPoint : cleanupPoints) {
2404-
cleanupPoint->eraseFromParent();
2423+
for (auto &cleanupPoint : cleanupPoints) {
2424+
cleanupPoint.inst->eraseFromParent();
24052425
}
24062426
if (auto state = stateOrAlias.dyn_cast<State*>()) {
24072427
delete state;

lib/SILGen/SILGenFunction.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
520520
// representation is demanded, but the addressable representation
521521
// gets demanded later, we save the insertion points where the
522522
// representation would be cleaned up so we can backfill them.
523-
llvm::SmallVector<SILInstruction*, 1> cleanupPoints;
523+
struct CleanupPoint {
524+
SILInstruction *inst;
525+
CleanupState state;
526+
};
527+
llvm::SmallVector<CleanupPoint, 1> cleanupPoints;
524528

525529
AddressableBuffer() = default;
526530

@@ -562,7 +566,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
562566
///
563567
/// This must be enclosed within the scope of the value binding for the
564568
/// variable, and cover the scope in which the variable can be referenced.
565-
void enterLocalVariableAddressableBufferScope(VarDecl *decl);
569+
///
570+
/// If \c destroyVarCleanup is provided, then the state of the referenced
571+
/// cleanup is tracked to determine whether the variable is initialized on
572+
/// every exit path out of the scope. Otherwise, the variable is assumed to
573+
/// be active on every exit.
574+
void enterLocalVariableAddressableBufferScope(VarDecl *decl,
575+
CleanupHandle destroyVarCleanup = CleanupHandle::invalid());
566576

567577
/// Get a stable address which is suitable for forming dependent pointers
568578
/// if possible.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-emit-silgen -enable-experimental-feature AddressableParameters %s
2+
3+
// REQUIRES: swift_feature_AddressableParameters
4+
5+
struct Foo {}
6+
7+
func foo(_: borrowing @_addressable Foo) {}
8+
9+
func bar() -> (Foo, Int)? { return nil }
10+
11+
func test1() {
12+
guard let (f, i) = bar() else {return}
13+
14+
foo(f)
15+
}
16+
17+
func test2() {
18+
if let (f, i) = bar() {foo(f)} else {}
19+
}

0 commit comments

Comments
 (0)