// Attempt to remove the array allocated at NewAddrValue and release its // refcounted elements. // // This is tightly coupled with the implementation of array.uninitialized. // The call to allocate an uninitialized array returns two values: // (Array<E> ArrayBase, UnsafeMutable<E> ArrayElementStorage) // // TODO: This relies on the lowest level array.uninitialized not being // inlined. To do better we could either run this pass before semantic inlining, // or we could also handle calls to array.init. static bool removeAndReleaseArray(SILValue NewArrayValue) { TupleExtractInst *ArrayDef = nullptr; TupleExtractInst *StorageAddress = nullptr; for (auto *Op : NewArrayValue->getUses()) { auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser()); if (!TupleElt) return false; switch (TupleElt->getFieldNo()) { default: return false; case 0: ArrayDef = TupleElt; break; case 1: StorageAddress = TupleElt; break; } } if (!ArrayDef) return false; // No Array object to delete. assert(!ArrayDef->getType().isTrivial(ArrayDef->getModule()) && "Array initialization should produce the proper tuple type."); // Analyze the array object uses. DeadObjectAnalysis DeadArray(ArrayDef); if (!DeadArray.analyze()) return false; // Require all stores to be into the array storage not the array object, // otherwise bail. bool HasStores = false; DeadArray.visitStoreLocations([&](ArrayRef<StoreInst*>){ HasStores = true; }); if (HasStores) return false; // Remove references to empty arrays. if (!StorageAddress) { removeInstructions(DeadArray.getAllUsers()); return true; } assert(StorageAddress->getType().isTrivial(ArrayDef->getModule()) && "Array initialization should produce the proper tuple type."); // Analyze the array storage uses. DeadObjectAnalysis DeadStorage(StorageAddress); if (!DeadStorage.analyze()) return false; // Find array object lifetime. ValueLifetimeAnalysis VLA(ArrayDef); ValueLifetime Lifetime = VLA.computeFromUserList(DeadArray.getAllUsers()); // Check that all storage users are in the Array's live blocks and never the // last user. for (auto *User : DeadStorage.getAllUsers()) { auto *BB = User->getParent(); if (!VLA.successorHasLiveIn(BB) && VLA.findLastSpecifiedUseInBlock(BB) == User) { return false; } } // For each store location, insert releases. // This makes a strong assumption that the allocated object is released on all // paths in which some object initialization occurs. SILSSAUpdater SSAUp; DeadStorage.visitStoreLocations([&] (ArrayRef<StoreInst*> Stores) { insertReleases(Stores, Lifetime.getLastUsers(), SSAUp); }); // Delete all uses of the dead array and its storage address. removeInstructions(DeadArray.getAllUsers()); removeInstructions(DeadStorage.getAllUsers()); return true; }
// Attempt to remove the array allocated at NewAddrValue and release its // refcounted elements. // // This is tightly coupled with the implementation of array.uninitialized. // The call to allocate an uninitialized array returns two values: // (Array<E> ArrayBase, UnsafeMutable<E> ArrayElementStorage) // // TODO: This relies on the lowest level array.uninitialized not being // inlined. To do better we could either run this pass before semantic inlining, // or we could also handle calls to array.init. static bool removeAndReleaseArray(SILValue NewArrayValue, bool &CFGChanged) { TupleExtractInst *ArrayDef = nullptr; TupleExtractInst *StorageAddress = nullptr; for (auto *Op : NewArrayValue->getUses()) { auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser()); if (!TupleElt) return false; if (TupleElt->getFieldNo() == 0 && !ArrayDef) { ArrayDef = TupleElt; } else if (TupleElt->getFieldNo() == 1 && !StorageAddress) { StorageAddress = TupleElt; } else { return false; } } if (!ArrayDef) return false; // No Array object to delete. assert(!ArrayDef->getType().isTrivial(ArrayDef->getModule()) && "Array initialization should produce the proper tuple type."); // Analyze the array object uses. DeadObjectAnalysis DeadArray(ArrayDef); if (!DeadArray.analyze()) return false; // Require all stores to be into the array storage not the array object, // otherwise bail. bool HasStores = false; DeadArray.visitStoreLocations([&](ArrayRef<StoreInst*>){ HasStores = true; }); if (HasStores) return false; // Remove references to empty arrays. if (!StorageAddress) { removeInstructions(DeadArray.getAllUsers()); return true; } assert(StorageAddress->getType().isTrivial(ArrayDef->getModule()) && "Array initialization should produce the proper tuple type."); // Analyze the array storage uses. DeadObjectAnalysis DeadStorage(StorageAddress); if (!DeadStorage.analyze()) return false; // Find array object lifetime. ValueLifetimeAnalysis VLA(NewArrayValue, DeadArray.getAllUsers()); // Check that all storage users are in the Array's live blocks. for (auto *User : DeadStorage.getAllUsers()) { if (!VLA.isWithinLifetime(User)) return false; } // For each store location, insert releases. // This makes a strong assumption that the allocated object is released on all // paths in which some object initialization occurs. SILSSAUpdater SSAUp; ValueLifetimeAnalysis::Frontier ArrayFrontier; CFGChanged |= !VLA.computeFrontier(ArrayFrontier, ValueLifetimeAnalysis::IgnoreExitEdges); DeadStorage.visitStoreLocations([&] (ArrayRef<StoreInst*> Stores) { insertReleases(Stores, ArrayFrontier, SSAUp); }); // Delete all uses of the dead array and its storage address. removeInstructions(DeadArray.getAllUsers()); removeInstructions(DeadStorage.getAllUsers()); return true; }