Skip to content

Move InstructionDeleter into its own header and fix iterator invalidation. #40243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/SILOptimizer/Utils/CFGOptUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"

namespace llvm {
template <typename T> class TinyPtrVector;
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/Statistic.h"

namespace swift {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SetVector.h"

Expand Down
196 changes: 19 additions & 177 deletions include/swift/SILOptimizer/Utils/InstOptUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
#include "swift/SILOptimizer/Utils/InstModCallbacks.h"
#include "swift/SILOptimizer/Utils/UpdatingInstructionIterator.h"
#include "llvm/ADT/SmallPtrSet.h"

namespace swift {
Expand Down Expand Up @@ -76,6 +75,20 @@ bool hasOnlyEndOfScopeOrEndOfLifetimeUses(SILInstruction *inst);
/// Return the number of @inout arguments passed to the given apply site.
unsigned getNumInOutArguments(FullApplySite applySite);

/// If \c inst is dead, delete it and recursively eliminate all code that
/// becomes dead because of that. If more than one instruction must
/// be checked/deleted use the \c InstructionDeleter utility.
///
/// This function will add necessary compensation code to fix the lifetimes of
/// the operands of the deleted instructions.
///
/// \pre the SIL function containing the instruction is assumed to be
/// consistent, i.e., does not have under or over releases.
///
/// \p callbacks.onDelete() is invoked to delete each instruction.
void eliminateDeadInstruction(SILInstruction *inst,
InstModCallbacks callbacks = InstModCallbacks());

/// For each of the given instructions, if they are dead delete them
/// along with their dead operands. Note this utility must be phased out and
/// replaced by \c eliminateDeadInstruction and
Expand All @@ -85,6 +98,8 @@ unsigned getNumInOutArguments(FullApplySite applySite);
/// \param force If Force is set, don't check if the top level instructions
/// are considered dead - delete them regardless.
/// \param callbacks The inst mod callbacks used to delete instructions.
///
/// Deprecated: Use InstructionDeleter instead.
void recursivelyDeleteTriviallyDeadInstructions(
ArrayRef<SILInstruction *> inst, bool force = false,
InstModCallbacks callbacks = InstModCallbacks());
Expand All @@ -98,6 +113,8 @@ void recursivelyDeleteTriviallyDeadInstructions(
/// \param force If Force is set, don't check if the top level instruction is
/// considered dead - delete it regardless.
/// \param callbacks InstModCallback used to delete instructions.
///
/// Deprecated: Use InstructionDeleter instead.
void recursivelyDeleteTriviallyDeadInstructions(
SILInstruction *inst, bool force = false,
InstModCallbacks callbacks = InstModCallbacks());
Expand Down Expand Up @@ -207,181 +224,6 @@ bool tryCheckedCastBrJumpThreading(
SmallVectorImpl<SILBasicBlock *> &blocksForWorklist,
bool EnableOSSARewriteTerminator);

/// A utility for deleting one or more instructions belonging to a function, and
/// cleaning up any dead code resulting from deleting those instructions. Use
/// this utility instead of \p recursivelyDeleteTriviallyDeadInstruction
/// as follows:
/// InstructionDeleter deleter;
/// deleter.deleteIfDead(instruction);
/// deleter.cleanupDeadInstructions();
///
/// This is designed to be used with a single 'onDelete' callback, which is
/// invoked consistently just before deleting each instruction. It's usually
/// used to avoid iterator invalidation (see the updatingIterator() factory
/// method). The other InstModCallbacks should generally be handled at a higher
/// level, and avoided altogether if possible. The following two are supported
/// for flexibility:
///
/// callbacks.createdNewInst() is invoked incrementally when it fixes lifetimes
/// while deleting a set of instructions, but the SIL may still be invalid
/// relative to the new instruction.
///
/// callbacks.notifyWillBeDeletedFunc() is invoked when a dead instruction is
/// first recognized and was not already passed in by the client. During the
/// callback, the to-be-deleted instruction has valid SIL. It's operands and
/// uses can be inspected and cached. It will be deleted later during
/// cleanupDeadInstructions().
///
/// Note that the forceDelete* APIs only invoke notifyWillBeDeletedFunc() when
/// an operand's definition will become dead after force-deleting the specified
/// instruction. Some clients force-delete related instructions one at a
/// time. It is the client's responsiblity to invoke notifyWillBeDeletedFunc()
/// on those explicitly deleted instructions if needed.
class InstructionDeleter {
/// A set vector of instructions that are found to be dead. The ordering of
/// instructions in this set is important as when a dead instruction is
/// removed, new instructions will be generated to fix the lifetime of the
/// instruction's operands. This has to be deterministic.
SmallSetVector<SILInstruction *, 8> deadInstructions;

UpdatingInstructionIteratorRegistry iteratorRegistry;

public:
InstructionDeleter(InstModCallbacks chainedCallbacks = InstModCallbacks())
: deadInstructions(), iteratorRegistry(chainedCallbacks) {}

UpdatingInstructionIteratorRegistry &getIteratorRegistry() {
return iteratorRegistry;
}

InstModCallbacks &getCallbacks() { return iteratorRegistry.getCallbacks(); }

llvm::iterator_range<UpdatingInstructionIterator>
updatingRange(SILBasicBlock *bb) {
return iteratorRegistry.makeIteratorRange(bb);
}

llvm::iterator_range<UpdatingReverseInstructionIterator>
updatingReverseRange(SILBasicBlock *bb) {
return iteratorRegistry.makeReverseIteratorRange(bb);
}

bool hadCallbackInvocation() const {
return const_cast<InstructionDeleter *>(this)
->getCallbacks()
.hadCallbackInvocation();
}

/// If the instruction \p inst is dead, record it so that it can be cleaned
/// up.
///
/// Calls callbacks.notifyWillBeDeleted().
bool trackIfDead(SILInstruction *inst);

/// Track this instruction as dead even if it has side effects. Used to enable
/// the deletion of a bunch of instructions at the same time.
///
/// Calls callbacks.notifyWillBeDeleted().
void forceTrackAsDead(SILInstruction *inst);

/// If the instruction \p inst is dead, delete it immediately along with its
/// destroys and scope-ending uses. If any operand definitions will become
/// dead after deleting this instruction, track them so they can be deleted
/// later during cleanUpDeadInstructions().
///
/// Calls callbacks.notifyWillBeDeleted().
bool deleteIfDead(SILInstruction *inst);

/// Delete the instruction \p inst, ignoring its side effects. If any operand
/// definitions will become dead after deleting this instruction, track them
/// so they can be deleted later during cleanUpDeadInstructions(). This
/// function will add necessary ownership instructions to fix the lifetimes of
/// the operands of \p inst to compensate for its deletion.
///
/// \pre the function containing \p inst must be using ownership SIL.
/// \pre the instruction to be deleted must not have any use other than
/// incidental uses.
///
/// callbacks.notifyWillBeDeleted will not be called for \p inst but will be
/// called for any other instructions that become dead as a result.
void forceDeleteAndFixLifetimes(SILInstruction *inst);

/// Delete the instruction \p inst and record instructions that may become
/// dead because of the removal of \c inst. If in ownership SIL, use the
/// \c forceDeleteAndFixLifetimes function instead, unless under special
/// circumstances where the client must handle fixing lifetimes of the
/// operands of the deleted instructions. This function will not fix the
/// lifetimes of the operands of \c inst once it is deleted. This function
/// will not clean up dead code resulting from the instruction's removal. To
/// do so, invoke the method \c cleanupDeadCode of this instance, once the SIL
/// of the contaning function is made consistent.
///
/// \pre the instruction to be deleted must not have any use other than
/// incidental uses.
///
/// callbacks.notifyWillBeDeleted will not be called for \p inst but will be
/// called for any other instructions that become dead as a result.
void forceDelete(SILInstruction *inst);

/// Recursively delete all of the uses of the instruction before deleting the
/// instruction itself. Does not fix lifetimes.
///
/// callbacks.notifyWillBeDeleted will not be called for \p inst but will
/// be called for any other instructions that become dead as a result.
void forceDeleteWithUsers(SILInstruction *inst) {
deleteWithUses(inst, /*fixLifetimes*/ false, /*forceDeleteUsers*/ true);
}

/// Clean up dead instructions that are tracked by this instance and all
/// instructions that transitively become dead.
///
/// \pre the function contaning dead instructions must be consistent (i.e., no
/// under or over releases). Note that if \c forceDelete call leaves the
/// function body in an inconsistent state, it needs to be made consistent
/// before this method is invoked.
///
/// callbacks.notifyWillBeDeletedFunc will only be called for instructions
/// that become dead during cleanup but were not already tracked.
void cleanupDeadInstructions();

/// Recursively visit users of \p inst and delete instructions that are dead
/// including \p inst.
///
/// callbacks.notifyWillBeDeletedFunc will be called for any dead
/// instructions.
void recursivelyDeleteUsersIfDead(SILInstruction *inst);

/// Recursively visit users of \p inst and force delete them including \p
/// inst. Also, destroy the consumed operands of the deleted instructions
/// whenever necessary.
///
/// callbacks.notifyWillBeDeletedFunc will not be called for \p inst or its
/// users but will be called for any other instructions that become dead as a
/// result.
void recursivelyForceDeleteUsersAndFixLifetimes(SILInstruction *inst);

private:
void deleteWithUses(SILInstruction *inst, bool fixLifetimes,
bool forceDeleteUsers = false);
};

/// If \c inst is dead, delete it and recursively eliminate all code that
/// becomes dead because of that. If more than one instruction must
/// be checked/deleted use the \c InstructionDeleter utility.
///
/// This function will add necessary compensation code to fix the lifetimes of
/// the operands of the deleted instructions.
///
/// \pre the SIL function containing the instruction is assumed to be
/// consistent, i.e., does not have under or over releases.
///
/// \p callbacks is used to delete each instruction. However, the callback
/// cannot be used to update instruction iterators since other instructions to
/// be deleted remain in the instruction list. If set to nullptr, we use the
/// default instruction modification callback structure.
void eliminateDeadInstruction(SILInstruction *inst,
InstModCallbacks callbacks = InstModCallbacks());

/// Get all consumed arguments of a partial_apply.
///
/// These are basically all arguments, except inout arguments and arguments
Expand Down Expand Up @@ -731,4 +573,4 @@ bool eliminateDeadAllocations(SILFunction &fn);

} // end namespace swift

#endif
#endif // SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H
Loading