Skip to content

Commit 76ab1c3

Browse files
committed
DeadObjectElimination: use InstructionDeleter to solve the instruction iterator invalidation problem
This simplifies the instruction iteration in processFunction().
1 parent bfef818 commit 76ab1c3

File tree

2 files changed

+108
-113
lines changed

2 files changed

+108
-113
lines changed

lib/SILOptimizer/Transforms/DeadObjectElimination.cpp

Lines changed: 108 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,6 @@ static bool doesDestructorHaveSideEffects(AllocRefInst *ARI) {
183183
return false;
184184
}
185185

186-
void static
187-
removeInstructions(ArrayRef<SILInstruction*> UsersToRemove) {
188-
for (auto *I : UsersToRemove) {
189-
I->replaceAllUsesOfAllResultsWithUndef();
190-
// Now we know that I should not have any uses... erase it from its parent.
191-
I->eraseFromParent();
192-
}
193-
}
194-
195186
//===----------------------------------------------------------------------===//
196187
// Use Graph Analysis
197188
//===----------------------------------------------------------------------===//
@@ -682,93 +673,6 @@ static void insertReleases(ArrayRef<StoreInst*> Stores,
682673
}
683674
}
684675

685-
// Attempt to remove the array allocated at NewAddrValue and release its
686-
// refcounted elements.
687-
//
688-
// This is tightly coupled with the implementation of array.uninitialized.
689-
// The call to allocate an uninitialized array returns two values:
690-
// (Array<E> ArrayBase, UnsafeMutable<E> ArrayElementStorage)
691-
//
692-
// TODO: This relies on the lowest level array.uninitialized not being
693-
// inlined. To do better we could either run this pass before semantic inlining,
694-
// or we could also handle calls to array.init.
695-
static bool removeAndReleaseArray(SingleValueInstruction *NewArrayValue,
696-
DeadEndBlocks &DEBlocks) {
697-
TupleExtractInst *ArrayDef = nullptr;
698-
TupleExtractInst *StorageAddress = nullptr;
699-
for (auto *Op : NewArrayValue->getUses()) {
700-
auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser());
701-
if (!TupleElt)
702-
return false;
703-
if (TupleElt->getFieldIndex() == 0 && !ArrayDef) {
704-
ArrayDef = TupleElt;
705-
} else if (TupleElt->getFieldIndex() == 1 && !StorageAddress) {
706-
StorageAddress = TupleElt;
707-
} else {
708-
return false;
709-
}
710-
}
711-
if (!ArrayDef)
712-
return false; // No Array object to delete.
713-
714-
assert(!ArrayDef->getType().isTrivial(*ArrayDef->getFunction()) &&
715-
"Array initialization should produce the proper tuple type.");
716-
717-
// Analyze the array object uses.
718-
DeadObjectAnalysis DeadArray(ArrayDef);
719-
if (!DeadArray.analyze())
720-
return false;
721-
722-
// Require all stores to be into the array storage not the array object,
723-
// otherwise bail.
724-
bool HasStores = false;
725-
DeadArray.visitStoreLocations([&](ArrayRef<StoreInst*>){ HasStores = true; });
726-
if (HasStores)
727-
return false;
728-
729-
// Remove references to empty arrays.
730-
if (!StorageAddress) {
731-
removeInstructions(DeadArray.getAllUsers());
732-
return true;
733-
}
734-
assert(StorageAddress->getType().isTrivial(*ArrayDef->getFunction()) &&
735-
"Array initialization should produce the proper tuple type.");
736-
737-
// Analyze the array storage uses.
738-
DeadObjectAnalysis DeadStorage(StorageAddress);
739-
if (!DeadStorage.analyze())
740-
return false;
741-
742-
// Find array object lifetime.
743-
ValueLifetimeAnalysis VLA(NewArrayValue, DeadArray.getAllUsers());
744-
745-
// Check that all storage users are in the Array's live blocks.
746-
for (auto *User : DeadStorage.getAllUsers()) {
747-
if (!VLA.isWithinLifetime(User))
748-
return false;
749-
}
750-
// For each store location, insert releases.
751-
SILSSAUpdater SSAUp;
752-
ValueLifetimeAnalysis::Frontier ArrayFrontier;
753-
if (!VLA.computeFrontier(ArrayFrontier,
754-
ValueLifetimeAnalysis::UsersMustPostDomDef,
755-
&DEBlocks)) {
756-
// In theory the allocated object must be released on all paths in which
757-
// some object initialization occurs. If not (for some reason) we bail.
758-
return false;
759-
}
760-
761-
DeadStorage.visitStoreLocations([&] (ArrayRef<StoreInst*> Stores) {
762-
insertReleases(Stores, ArrayFrontier, SSAUp);
763-
});
764-
765-
// Delete all uses of the dead array and its storage address.
766-
removeInstructions(DeadArray.getAllUsers());
767-
removeInstructions(DeadStorage.getAllUsers());
768-
769-
return true;
770-
}
771-
772676
//===----------------------------------------------------------------------===//
773677
// Function Processing
774678
//===----------------------------------------------------------------------===//
@@ -785,12 +689,21 @@ namespace {
785689
class DeadObjectElimination : public SILFunctionTransform {
786690
llvm::DenseMap<SILType, bool> DestructorAnalysisCache;
787691

692+
InstructionDeleter deleter;
693+
694+
void removeInstructions(ArrayRef<SILInstruction*> toRemove);
695+
788696
bool processAllocRef(AllocRefInst *ARI);
789697
bool processAllocStack(AllocStackInst *ASI);
790698
bool processKeyPath(KeyPathInst *KPI);
791699
bool processAllocBox(AllocBoxInst *ABI){ return false;}
792700
bool processAllocApply(ApplyInst *AI, DeadEndBlocks &DEBlocks);
793701

702+
bool getDeadInstsAfterInitializerRemoved(
703+
ApplyInst *AI, llvm::SmallVectorImpl<SILInstruction *> &ToDestroy);
704+
bool removeAndReleaseArray(
705+
SingleValueInstruction *NewArrayValue, DeadEndBlocks &DEBlocks);
706+
794707
bool processFunction(SILFunction &Fn) {
795708
DeadEndBlocks DEBlocks(&Fn);
796709
DestructorAnalysisCache.clear();
@@ -799,19 +712,7 @@ class DeadObjectElimination : public SILFunctionTransform {
799712

800713
for (auto &BB : Fn) {
801714

802-
llvm::SmallVector<SILInstruction*, 64> instsToProcess;
803-
for (SILInstruction &inst : BB) {
804-
if (isa<AllocationInst>(&inst) ||
805-
isAllocatingApply(&inst) ||
806-
isa<KeyPathInst>(&inst)) {
807-
instsToProcess.push_back(&inst);
808-
}
809-
}
810-
811-
for (SILInstruction *inst : instsToProcess) {
812-
if (inst->isDeleted())
813-
continue;
814-
715+
for (SILInstruction *inst : deleter.updatingRange(&BB)) {
815716
if (auto *A = dyn_cast<AllocRefInst>(inst))
816717
Changed |= processAllocRef(A);
817718
else if (auto *A = dyn_cast<AllocStackInst>(inst))
@@ -823,6 +724,7 @@ class DeadObjectElimination : public SILFunctionTransform {
823724
else if (auto *A = dyn_cast<ApplyInst>(inst))
824725
Changed |= processAllocApply(A, DEBlocks);
825726
}
727+
deleter.cleanupDeadInstructions();
826728
}
827729
return Changed;
828730
}
@@ -840,6 +742,15 @@ class DeadObjectElimination : public SILFunctionTransform {
840742
};
841743
} // end anonymous namespace
842744

745+
void
746+
DeadObjectElimination::removeInstructions(ArrayRef<SILInstruction*> toRemove) {
747+
for (auto *I : toRemove) {
748+
I->replaceAllUsesOfAllResultsWithUndef();
749+
// Now we know that I should not have any uses... erase it from its parent.
750+
deleter.forceDelete(I);
751+
}
752+
}
753+
843754
bool DeadObjectElimination::processAllocRef(AllocRefInst *ARI) {
844755
// Ok, we have an alloc_ref. Check the cache to see if we have already
845756
// computed the destructor behavior for its SILType.
@@ -932,7 +843,7 @@ bool DeadObjectElimination::processKeyPath(KeyPathInst *KPI) {
932843
/// If AI is the version of an initializer where we pass in either an apply or
933844
/// an alloc_ref to initialize in place, validate that we are able to continue
934845
/// optimizing and return To
935-
static bool getDeadInstsAfterInitializerRemoved(
846+
bool DeadObjectElimination::getDeadInstsAfterInitializerRemoved(
936847
ApplyInst *AI, llvm::SmallVectorImpl<SILInstruction *> &ToDestroy) {
937848
assert(ToDestroy.empty() && "We assume that ToDestroy is empty, so on "
938849
"failure we can clear without worrying about the "
@@ -973,6 +884,93 @@ static bool getDeadInstsAfterInitializerRemoved(
973884
return true;
974885
}
975886

887+
// Attempt to remove the array allocated at NewAddrValue and release its
888+
// refcounted elements.
889+
//
890+
// This is tightly coupled with the implementation of array.uninitialized.
891+
// The call to allocate an uninitialized array returns two values:
892+
// (Array<E> ArrayBase, UnsafeMutable<E> ArrayElementStorage)
893+
//
894+
// TODO: This relies on the lowest level array.uninitialized not being
895+
// inlined. To do better we could either run this pass before semantic inlining,
896+
// or we could also handle calls to array.init.
897+
bool DeadObjectElimination::removeAndReleaseArray(
898+
SingleValueInstruction *NewArrayValue, DeadEndBlocks &DEBlocks) {
899+
TupleExtractInst *ArrayDef = nullptr;
900+
TupleExtractInst *StorageAddress = nullptr;
901+
for (auto *Op : NewArrayValue->getUses()) {
902+
auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser());
903+
if (!TupleElt)
904+
return false;
905+
if (TupleElt->getFieldIndex() == 0 && !ArrayDef) {
906+
ArrayDef = TupleElt;
907+
} else if (TupleElt->getFieldIndex() == 1 && !StorageAddress) {
908+
StorageAddress = TupleElt;
909+
} else {
910+
return false;
911+
}
912+
}
913+
if (!ArrayDef)
914+
return false; // No Array object to delete.
915+
916+
assert(!ArrayDef->getType().isTrivial(*ArrayDef->getFunction()) &&
917+
"Array initialization should produce the proper tuple type.");
918+
919+
// Analyze the array object uses.
920+
DeadObjectAnalysis DeadArray(ArrayDef);
921+
if (!DeadArray.analyze())
922+
return false;
923+
924+
// Require all stores to be into the array storage not the array object,
925+
// otherwise bail.
926+
bool HasStores = false;
927+
DeadArray.visitStoreLocations([&](ArrayRef<StoreInst*>){ HasStores = true; });
928+
if (HasStores)
929+
return false;
930+
931+
// Remove references to empty arrays.
932+
if (!StorageAddress) {
933+
removeInstructions(DeadArray.getAllUsers());
934+
return true;
935+
}
936+
assert(StorageAddress->getType().isTrivial(*ArrayDef->getFunction()) &&
937+
"Array initialization should produce the proper tuple type.");
938+
939+
// Analyze the array storage uses.
940+
DeadObjectAnalysis DeadStorage(StorageAddress);
941+
if (!DeadStorage.analyze())
942+
return false;
943+
944+
// Find array object lifetime.
945+
ValueLifetimeAnalysis VLA(NewArrayValue, DeadArray.getAllUsers());
946+
947+
// Check that all storage users are in the Array's live blocks.
948+
for (auto *User : DeadStorage.getAllUsers()) {
949+
if (!VLA.isWithinLifetime(User))
950+
return false;
951+
}
952+
// For each store location, insert releases.
953+
SILSSAUpdater SSAUp;
954+
ValueLifetimeAnalysis::Frontier ArrayFrontier;
955+
if (!VLA.computeFrontier(ArrayFrontier,
956+
ValueLifetimeAnalysis::UsersMustPostDomDef,
957+
&DEBlocks)) {
958+
// In theory the allocated object must be released on all paths in which
959+
// some object initialization occurs. If not (for some reason) we bail.
960+
return false;
961+
}
962+
963+
DeadStorage.visitStoreLocations([&] (ArrayRef<StoreInst*> Stores) {
964+
insertReleases(Stores, ArrayFrontier, SSAUp);
965+
});
966+
967+
// Delete all uses of the dead array and its storage address.
968+
removeInstructions(DeadArray.getAllUsers());
969+
removeInstructions(DeadStorage.getAllUsers());
970+
971+
return true;
972+
}
973+
976974
bool DeadObjectElimination::processAllocApply(ApplyInst *AI,
977975
DeadEndBlocks &DEBlocks) {
978976
// Currently only handle array.uninitialized
@@ -989,12 +987,10 @@ bool DeadObjectElimination::processAllocApply(ApplyInst *AI,
989987

990988
LLVM_DEBUG(llvm::dbgs() << " Success! Eliminating apply allocate(...).\n");
991989

992-
InstructionDeleter deleter;
993990
deleter.forceDeleteWithUsers(AI);
994991
for (auto *toDelete : instsDeadAfterInitializerRemoved) {
995992
deleter.trackIfDead(toDelete);
996993
}
997-
deleter.cleanupDeadInstructions();
998994

999995
++DeadAllocApplyEliminated;
1000996
return true;

test/SILOptimizer/dead_alloc_elim.sil

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ sil @trivial_destructor_load : $@convention(thin) () -> () {
164164
//
165165
// CHECK-LABEL: sil @trivial_destructor_store_into : $@convention(thin) () -> () {
166166
// CHECK: bb0
167-
// CHECK-NEXT: integer_literal
168167
// CHECK-NEXT: tuple
169168
// CHECK-NEXT: return
170169
sil @trivial_destructor_store_into : $@convention(thin) () -> () {

0 commit comments

Comments
 (0)