Skip to content

Commit 0de36ba

Browse files
authored
Merge pull request #2338 from swiftwasm/main
[pull] swiftwasm from main
2 parents a51dc3b + 8dec996 commit 0de36ba

File tree

2 files changed

+80
-5
lines changed

2 files changed

+80
-5
lines changed

lib/SILOptimizer/Transforms/DeadObjectElimination.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,21 +251,24 @@ static bool canZapInstruction(SILInstruction *Inst, bool acceptRefCountInsts,
251251
/// Analyze the use graph of AllocRef for any uses that would prevent us from
252252
/// zapping it completely.
253253
static bool
254-
hasUnremovableUsers(SILInstruction *AllocRef, UserList &Users,
254+
hasUnremovableUsers(SILInstruction *AllocRef, UserList *Users,
255255
bool acceptRefCountInsts, bool onlyAcceptTrivialStores) {
256256
SmallVector<SILInstruction *, 16> Worklist;
257257
Worklist.push_back(AllocRef);
258258

259259
LLVM_DEBUG(llvm::dbgs() << " Analyzing Use Graph.");
260260

261+
SmallVector<RefElementAddrInst *, 8> refElementAddrs;
262+
bool deallocationMaybeInlined = false;
263+
261264
while (!Worklist.empty()) {
262265
SILInstruction *I = Worklist.pop_back_val();
263266

264267
LLVM_DEBUG(llvm::dbgs() << " Visiting: " << *I);
265268

266269
// Insert the instruction into our InvolvedInstructions set. If we have
267270
// already seen it, then don't reprocess all of the uses.
268-
if (!Users.insert(I)) {
271+
if (Users && !Users->insert(I)) {
269272
LLVM_DEBUG(llvm::dbgs() << " Already seen skipping...\n");
270273
continue;
271274
}
@@ -276,6 +279,13 @@ hasUnremovableUsers(SILInstruction *AllocRef, UserList &Users,
276279
return true;
277280
}
278281

282+
if (auto *rea = dyn_cast<RefElementAddrInst>(I)) {
283+
if (!rea->getType().isTrivial(*rea->getFunction()))
284+
refElementAddrs.push_back(rea);
285+
} else if (isa<SetDeallocatingInst>(I)) {
286+
deallocationMaybeInlined = true;
287+
}
288+
279289
// At this point, we can remove the instruction as long as all of its users
280290
// can be removed as well. Scan its users and add them to the worklist for
281291
// recursive processing.
@@ -299,6 +309,20 @@ hasUnremovableUsers(SILInstruction *AllocRef, UserList &Users,
299309
}
300310
}
301311

312+
if (deallocationMaybeInlined) {
313+
// The alloc_ref is not destructed by a strong_release which is calling the
314+
// deallocator (destroying all stored properties).
315+
// In non-OSSA we cannot reliably track the lifetime of non-trivial stored
316+
// properties. Removing the dead alloc_ref might leak a property value.
317+
// TODO: in OSSA we can replace stores to properties with a destroy_value.
318+
for (RefElementAddrInst *rea : refElementAddrs) {
319+
// Re-run the check with not accepting non-trivial stores.
320+
if (hasUnremovableUsers(rea, nullptr, acceptRefCountInsts,
321+
/*onlyAcceptTrivialStores*/ true))
322+
return true;
323+
}
324+
}
325+
302326
return false;
303327
}
304328

@@ -736,7 +760,7 @@ bool DeadObjectElimination::processAllocRef(AllocRefInst *ARI) {
736760
// Our destructor has no side effects, so if we can prove that no loads
737761
// escape, then we can completely remove the use graph of this alloc_ref.
738762
UserList UsersToRemove;
739-
if (hasUnremovableUsers(ARI, UsersToRemove,
763+
if (hasUnremovableUsers(ARI, &UsersToRemove,
740764
/*acceptRefCountInsts=*/ !HasSideEffects,
741765
/*onlyAcceptTrivialStores*/false)) {
742766
LLVM_DEBUG(llvm::dbgs() << " Found a use that cannot be zapped...\n");
@@ -756,7 +780,7 @@ bool DeadObjectElimination::processAllocStack(AllocStackInst *ASI) {
756780
// Trivial types don't have destructors.
757781
bool isTrivialType = ASI->getElementType().isTrivial(*ASI->getFunction());
758782
UserList UsersToRemove;
759-
if (hasUnremovableUsers(ASI, UsersToRemove, /*acceptRefCountInsts=*/ true,
783+
if (hasUnremovableUsers(ASI, &UsersToRemove, /*acceptRefCountInsts=*/ true,
760784
/*onlyAcceptTrivialStores*/!isTrivialType)) {
761785
LLVM_DEBUG(llvm::dbgs() << " Found a use that cannot be zapped...\n");
762786
return false;
@@ -773,7 +797,7 @@ bool DeadObjectElimination::processAllocStack(AllocStackInst *ASI) {
773797

774798
bool DeadObjectElimination::processKeyPath(KeyPathInst *KPI) {
775799
UserList UsersToRemove;
776-
if (hasUnremovableUsers(KPI, UsersToRemove, /*acceptRefCountInsts=*/ true,
800+
if (hasUnremovableUsers(KPI, &UsersToRemove, /*acceptRefCountInsts=*/ true,
777801
/*onlyAcceptTrivialStores*/ false)) {
778802
LLVM_DEBUG(llvm::dbgs() << " Found a use that cannot be zapped...\n");
779803
return false;

test/SILOptimizer/dead_alloc_elim.sil

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ class TrivialDestructor {
1414
deinit { }
1515
}
1616

17+
class Kl {}
18+
1719
class NontrivialDestructor {
20+
@_hasStorage var p : Kl
21+
@_hasStorage var i : Int
1822
init()
1923
}
2024

@@ -79,6 +83,53 @@ sil @devirtualized_destructor : $@convention(thin) () -> () {
7983
return %1 : $()
8084
}
8185

86+
// In non-OSSA we cannot remove alloc_refs with stores to non-trivial properties.
87+
//
88+
// CHECK-LABEL: sil @store_to_non_trivial_property1
89+
// CHECK: alloc_ref [stack] $NontrivialDestructor
90+
// CHECK: } // end sil function 'store_to_non_trivial_property1'
91+
sil @store_to_non_trivial_property1 : $@convention(thin) (@owned Kl) -> () {
92+
bb0(%0 : $Kl):
93+
%20 = alloc_ref [stack] $NontrivialDestructor
94+
%21 = ref_element_addr %20 : $NontrivialDestructor, #NontrivialDestructor.p
95+
store %0 to %21 : $*Kl
96+
set_deallocating %20 : $NontrivialDestructor
97+
destroy_addr %21 : $*Kl
98+
dealloc_ref [stack] %20 : $NontrivialDestructor
99+
%r = tuple ()
100+
return %r : $()
101+
}
102+
103+
// CHECK-LABEL: sil @store_to_non_trivial_property2
104+
// CHECK: alloc_ref [stack] $NontrivialDestructor
105+
// CHECK: } // end sil function 'store_to_non_trivial_property2'
106+
sil @store_to_non_trivial_property2 : $@convention(thin) (@owned Kl) -> () {
107+
bb0(%0 : $Kl):
108+
%20 = alloc_ref [stack] $NontrivialDestructor
109+
%21 = ref_element_addr %20 : $NontrivialDestructor, #NontrivialDestructor.p
110+
store %0 to %21 : $*Kl
111+
strong_release %20 : $NontrivialDestructor
112+
dealloc_ref [stack] %20 : $NontrivialDestructor
113+
%r = tuple ()
114+
return %r : $()
115+
}
116+
117+
// CHECK-LABEL: sil @store_to_trivial_property
118+
// CHECK-NOT: alloc_ref
119+
// CHECK: } // end sil function 'store_to_trivial_property'
120+
sil @store_to_trivial_property : $@convention(thin) (Int) -> () {
121+
bb0(%0 : $Int):
122+
%20 = alloc_ref [stack] $NontrivialDestructor
123+
%21 = ref_element_addr %20 : $NontrivialDestructor, #NontrivialDestructor.i
124+
store %0 to %21 : $*Int
125+
set_deallocating %20 : $NontrivialDestructor
126+
dealloc_ref %20 : $NontrivialDestructor
127+
dealloc_ref [stack] %20 : $NontrivialDestructor
128+
%r = tuple ()
129+
return %r : $()
130+
}
131+
132+
82133
// We load/use a pointer from the alloc_ref, do nothing.
83134
//
84135
// CHECK-LABEL: sil @trivial_destructor_load : $@convention(thin) () -> () {

0 commit comments

Comments
 (0)