mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
InstModCallback is a value type and as such the original callback struct is not being modified. Instead, a new InstModCallback struct is returned that is the old callback + assignment of the passed in callback to the appropriate field. Thus it makes sense to put this attribute on these methods so that we get a warning if one does not use the new returned callback (otherwise, why would one call this method?!). More likely a bug.
796 lines
34 KiB
C++
796 lines
34 KiB
C++
//===--- InstOptUtils.h - SILOptimizer instruction utilities ----*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// Utilities used by the SILOptimizer for analyzing and transforming
|
|
/// SILInstructions.
|
|
///
|
|
/// SIL/InstUtils.h provides essential SILInstruction utilities.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H
|
|
#define SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H
|
|
|
|
#include "swift/SIL/BasicBlockUtils.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include <functional>
|
|
#include <utility>
|
|
|
|
namespace swift {
|
|
|
|
class DominanceInfo;
|
|
template <class T> class NullablePtr;
|
|
|
|
/// A structure containing callbacks that are called when an instruction is
|
|
/// removed or added.
|
|
///
|
|
/// PERFORMANCE NOTES: This code can be used in loops, so we want to make sure
|
|
/// to not have overhead when the user does not specify a callback. To do that
|
|
/// instead of defining a "default" std::function, we represent the "default"
|
|
/// functions as nullptr. Then, in the helper function trampoline that actually
|
|
/// gets called, we check if we have a nullptr and if we do, we perform the
|
|
/// default operation inline. What is nice about this from a perf perspective is
|
|
/// that in a loop this property should predict well since you have a single
|
|
/// branch that is going to go the same way everytime.
|
|
class InstModCallbacks {
|
|
/// A function that is called to notify that a new function was created.
|
|
///
|
|
/// Default implementation is a no-op, but we still mark madeChange.
|
|
std::function<void(SILInstruction *newlyCreatedInst)> createdNewInstFunc;
|
|
|
|
/// A function sets the value in \p use to be \p newValue.
|
|
///
|
|
/// Default implementation just calls use->set(newValue).
|
|
///
|
|
/// NOTE: It is assumed that this operation will never invalidate instruction
|
|
/// iterators.
|
|
std::function<void(Operand *use, SILValue newValue)> setUseValueFunc;
|
|
|
|
/// A function that takes in an instruction and deletes the inst.
|
|
///
|
|
/// Default implementation is instToDelete->eraseFromParent();
|
|
///
|
|
/// NOTE: The reason why we have deleteInstFunc and notifyWillBeDeletedFunc is
|
|
/// InstModCallback supports 2 stage deletion where a callee passed
|
|
/// InstModCallback is allowed to drop all references to the instruction
|
|
/// before calling deleteInstFunc. In contrast, notifyWillBeDeletedFunc
|
|
/// assumes that the IR is in a good form before being called so that the
|
|
/// caller can via the callback gather state about the instruction that will
|
|
/// be deleted. As an example, see InstructionDeleter::deleteInstruction() in
|
|
/// InstOptUtils.cpp.
|
|
std::function<void(SILInstruction *instToDelete)> deleteInstFunc;
|
|
|
|
/// If non-null, called before an instruction is deleted or has its references
|
|
/// dropped. If null, no-op.
|
|
///
|
|
/// NOTE: The reason why we have deleteInstFunc and notifyWillBeDeletedFunc is
|
|
/// InstModCallback supports 2 stage deletion where a callee passed
|
|
/// InstModCallback is allowed to drop all references to the instruction
|
|
/// before calling deleteInstFunc. In contrast, notifyWillBeDeletedFunc
|
|
/// assumes that the IR is in a good form before being called so that the
|
|
/// caller can via the callback gather state about the instruction that will
|
|
/// be deleted. As an example, see InstructionDeleter::deleteInstruction() in
|
|
/// InstOptUtils.cpp.
|
|
///
|
|
/// NOTE: This is called in InstModCallback::deleteInst() if one does not use
|
|
/// a default bool argument to disable the notification. In general that
|
|
/// should only be done when one is writing custom handling and is performing
|
|
/// the notification ones self. It is assumed that the notification will be
|
|
/// called with a valid instruction.
|
|
std::function<void(SILInstruction *instThatWillBeDeleted)>
|
|
notifyWillBeDeletedFunc;
|
|
|
|
/// A boolean that tracks if any of our callbacks were ever called.
|
|
bool wereAnyCallbacksInvoked = false;
|
|
|
|
public:
|
|
InstModCallbacks() = default;
|
|
~InstModCallbacks() = default;
|
|
InstModCallbacks(const InstModCallbacks &) = default;
|
|
|
|
/// Return a copy of self with deleteInstFunc set to \p newDeleteInstFunc.
|
|
LLVM_ATTRIBUTE_UNUSED InstModCallbacks
|
|
onDelete(decltype(deleteInstFunc) newDeleteInstFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.deleteInstFunc = newDeleteInstFunc;
|
|
return result;
|
|
}
|
|
|
|
/// Return a copy of self with createdNewInstFunc set to \p
|
|
/// newCreatedNewInstFunc.
|
|
LLVM_ATTRIBUTE_UNUSED InstModCallbacks
|
|
onCreateNewInst(decltype(createdNewInstFunc) newCreatedNewInstFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.createdNewInstFunc = newCreatedNewInstFunc;
|
|
return result;
|
|
}
|
|
|
|
/// Return a copy of self with setUseValueFunc set to \p newSetUseValueFunc.
|
|
LLVM_ATTRIBUTE_UNUSED InstModCallbacks
|
|
onSetUseValue(decltype(setUseValueFunc) newSetUseValueFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.setUseValueFunc = newSetUseValueFunc;
|
|
return result;
|
|
}
|
|
|
|
/// Return a copy of self with notifyWillBeDeletedFunc set to \p
|
|
/// newNotifyWillBeDeletedFunc.
|
|
LLVM_ATTRIBUTE_UNUSED
|
|
InstModCallbacks onNotifyWillBeDeleted(
|
|
decltype(notifyWillBeDeletedFunc) newNotifyWillBeDeletedFunc) const {
|
|
InstModCallbacks result = *this;
|
|
result.notifyWillBeDeletedFunc = newNotifyWillBeDeletedFunc;
|
|
return result;
|
|
}
|
|
|
|
void deleteInst(SILInstruction *instToDelete,
|
|
bool notifyWhenDeleting = true) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (notifyWhenDeleting && notifyWillBeDeletedFunc)
|
|
notifyWillBeDeletedFunc(instToDelete);
|
|
if (deleteInstFunc)
|
|
return deleteInstFunc(instToDelete);
|
|
instToDelete->eraseFromParent();
|
|
}
|
|
|
|
void createdNewInst(SILInstruction *newlyCreatedInst) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (createdNewInstFunc)
|
|
createdNewInstFunc(newlyCreatedInst);
|
|
}
|
|
|
|
void setUseValue(Operand *use, SILValue newValue) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (setUseValueFunc)
|
|
return setUseValueFunc(use, newValue);
|
|
use->set(newValue);
|
|
}
|
|
|
|
/// Notify via our callbacks that an instruction will be deleted/have its
|
|
/// operands dropped.
|
|
///
|
|
/// DISCUSSION: Since we do not delete instructions in any specific order, we
|
|
/// drop all references of the instructions before we call deleteInst. Thus
|
|
/// one can not in deleteInst look at operands. Certain parts of the optimizer
|
|
/// rely on this ability, so we preserve it.
|
|
void notifyWillBeDeleted(SILInstruction *instThatWillBeDeleted) {
|
|
wereAnyCallbacksInvoked = true;
|
|
if (notifyWillBeDeletedFunc)
|
|
return notifyWillBeDeletedFunc(instThatWillBeDeleted);
|
|
}
|
|
|
|
void replaceValueUsesWith(SILValue oldValue, SILValue newValue) {
|
|
wereAnyCallbacksInvoked = true;
|
|
|
|
// If setUseValueFunc is not set, just call RAUW directly. RAUW in this case
|
|
// is equivalent to what we do below. We just enable better
|
|
// performance. This ensures that the default InstModCallback is really
|
|
// fast.
|
|
if (!setUseValueFunc)
|
|
return oldValue->replaceAllUsesWith(newValue);
|
|
|
|
while (!oldValue->use_empty()) {
|
|
auto *use = *oldValue->use_begin();
|
|
setUseValue(use, newValue);
|
|
}
|
|
}
|
|
|
|
void eraseAndRAUWSingleValueInst(SingleValueInstruction *oldInst,
|
|
SILValue newValue) {
|
|
wereAnyCallbacksInvoked = true;
|
|
replaceValueUsesWith(oldInst, newValue);
|
|
deleteInst(oldInst);
|
|
}
|
|
|
|
bool hadCallbackInvocation() const { return wereAnyCallbacksInvoked; }
|
|
|
|
/// Set \p wereAnyCallbacksInvoked to false. Useful if one wants to reuse an
|
|
/// InstModCallback in between iterations.
|
|
void resetHadCallbackInvocation() { wereAnyCallbacksInvoked = false; }
|
|
};
|
|
|
|
/// Transform a Use Range (Operand*) into a User Range (SILInstruction *)
|
|
using UserTransform = std::function<SILInstruction *(Operand *)>;
|
|
using ValueBaseUserRange =
|
|
TransformRange<iterator_range<ValueBase::use_iterator>, UserTransform>;
|
|
|
|
template <typename Range>
|
|
inline TransformRange<Range, UserTransform> makeUserRange(Range range) {
|
|
auto toUser = [](Operand *operand) { return operand->getUser(); };
|
|
return makeTransformRange(range, UserTransform(toUser));
|
|
}
|
|
|
|
/// Transform a use_iterator range (Operand*) into an llvm::iterator_range
|
|
/// of users (SILInstruction *)
|
|
inline iterator_range<llvm::mapped_iterator<ValueBase::use_iterator, UserTransform>>
|
|
makeUserIteratorRange(iterator_range<ValueBase::use_iterator> useRange) {
|
|
auto toUser = [](Operand *operand) { return operand->getUser(); };
|
|
return llvm::map_range(useRange, UserTransform(toUser));
|
|
}
|
|
|
|
using DeadInstructionSet = llvm::SmallSetVector<SILInstruction *, 8>;
|
|
|
|
/// Create a retain of \p Ptr before the \p InsertPt.
|
|
NullablePtr<SILInstruction> createIncrementBefore(SILValue ptr,
|
|
SILInstruction *insertpt);
|
|
|
|
/// Create a release of \p Ptr before the \p InsertPt.
|
|
NullablePtr<SILInstruction> createDecrementBefore(SILValue ptr,
|
|
SILInstruction *insertpt);
|
|
|
|
/// Get the insertion point after \p val.
|
|
Optional<SILBasicBlock::iterator> getInsertAfterPoint(SILValue val);
|
|
|
|
/// Return the number of @inout arguments passed to the given apply site.
|
|
unsigned getNumInOutArguments(FullApplySite applySite);
|
|
|
|
/// 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
|
|
/// \c InstructionDeleter utilities.
|
|
///
|
|
/// \param inst The ArrayRef of instructions to be deleted.
|
|
/// \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.
|
|
void recursivelyDeleteTriviallyDeadInstructions(
|
|
ArrayRef<SILInstruction *> inst, bool force = false,
|
|
InstModCallbacks callbacks = InstModCallbacks());
|
|
|
|
/// If the given instruction is dead, delete it along with its dead
|
|
/// operands. Note this utility must be phased out and replaced by
|
|
/// \c eliminateDeadInstruction and
|
|
/// \c InstructionDeleter utilities.
|
|
///
|
|
/// \param inst The instruction to be deleted.
|
|
/// \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.
|
|
void recursivelyDeleteTriviallyDeadInstructions(
|
|
SILInstruction *inst, bool force = false,
|
|
InstModCallbacks callbacks = InstModCallbacks());
|
|
|
|
/// Perform a fast local check to see if the instruction is dead.
|
|
///
|
|
/// This routine only examines the state of the instruction at hand.
|
|
bool isInstructionTriviallyDead(SILInstruction *inst);
|
|
|
|
/// Return true if this is a release instruction that's not going to
|
|
/// free the object.
|
|
bool isIntermediateRelease(SILInstruction *inst, EpilogueARCFunctionInfo *erfi);
|
|
|
|
/// Recursively collect all the uses and transitive uses of the
|
|
/// instruction.
|
|
void collectUsesOfValue(SILValue V,
|
|
llvm::SmallPtrSetImpl<SILInstruction *> &Insts);
|
|
|
|
/// Recursively erase all of the uses of the instruction (but not the
|
|
/// instruction itself)
|
|
void eraseUsesOfInstruction(SILInstruction *inst,
|
|
InstModCallbacks callbacks = InstModCallbacks());
|
|
|
|
/// Recursively erase all of the uses of the value (but not the
|
|
/// value itself)
|
|
void eraseUsesOfValue(SILValue value);
|
|
|
|
/// Gets the concrete value which is stored in an existential box.
|
|
/// Returns %value in following pattern:
|
|
///
|
|
/// %existentialBox = alloc_existential_box $Error, $ConcreteError
|
|
/// %a = project_existential_box $ConcreteError in %existentialBox : $Error
|
|
/// store %value to %a : $*ConcreteError
|
|
///
|
|
/// Returns an invalid SILValue in case there are multiple stores or any unknown
|
|
/// users of \p existentialBox.
|
|
/// The \p ignoreUser is ignored in the user list of \p existentialBox.
|
|
SILValue
|
|
getConcreteValueOfExistentialBox(AllocExistentialBoxInst *existentialBox,
|
|
SILInstruction *ignoreUser);
|
|
|
|
/// Gets the concrete value which is stored in an existential box, which itself
|
|
/// is stored in \p addr.
|
|
/// Returns %value in following pattern:
|
|
///
|
|
/// %b = alloc_existential_box $Error, $ConcreteError
|
|
/// %a = project_existential_box $ConcreteError in %b : $Error
|
|
/// store %value to %a : $*ConcreteError
|
|
/// %addr = alloc_stack $Error
|
|
/// store %b to %addr : $*Error
|
|
///
|
|
/// Returns an invalid SILValue in case there are multiple stores or any unknown
|
|
/// users of \p addr or the existential box.
|
|
/// The \p ignoreUser is ignored in the user list of \p addr.
|
|
SILValue getConcreteValueOfExistentialBoxAddr(SILValue addr,
|
|
SILInstruction *ignoreUser);
|
|
|
|
/// Cast a value into the expected, ABI compatible type if necessary.
|
|
/// This may happen e.g. when:
|
|
/// - a type of the return value is a subclass of the expected return type.
|
|
/// - actual return type and expected return type differ in optionality.
|
|
/// - both types are tuple-types and some of the elements need to be casted.
|
|
///
|
|
/// \p usePoints is required when \p value has guaranteed ownership. It must be
|
|
/// the last users of the returned, casted value. A usePoint cannot be a
|
|
/// BranchInst (a phi is never the last guaranteed user). \p builder's current
|
|
/// insertion point must dominate all \p usePoints.
|
|
std::pair<SILValue, bool /* changedCFG */>
|
|
castValueToABICompatibleType(SILBuilder *builder, SILLocation Loc,
|
|
SILValue value, SILType srcTy, SILType destTy,
|
|
ArrayRef<SILInstruction *> usePoints);
|
|
/// Peek through trivial Enum initialization, typically for pointless
|
|
/// Optionals.
|
|
///
|
|
/// The returned InitEnumDataAddr dominates the given
|
|
/// UncheckedTakeEnumDataAddrInst.
|
|
InitEnumDataAddrInst *
|
|
findInitAddressForTrivialEnum(UncheckedTakeEnumDataAddrInst *utedai);
|
|
|
|
/// Returns a project_box if it is the next instruction after \p ABI and
|
|
/// and has \p ABI as operand. Otherwise it creates a new project_box right
|
|
/// after \p ABI and returns it.
|
|
ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *abi, unsigned index);
|
|
|
|
/// Return true if any call inside the given function may bind dynamic
|
|
/// 'Self' to a generic argument of the callee.
|
|
bool mayBindDynamicSelf(SILFunction *f);
|
|
|
|
/// Check whether the \p addr is an address of a tail-allocated array element.
|
|
bool isAddressOfArrayElement(SILValue addr);
|
|
|
|
/// Move an ApplyInst's FuncRef so that it dominates the call site.
|
|
void placeFuncRef(ApplyInst *ai, DominanceInfo *dt);
|
|
|
|
/// Add an argument, \p val, to the branch-edge that is pointing into
|
|
/// block \p Dest. Return a new instruction and do not erase the old
|
|
/// instruction.
|
|
TermInst *addArgumentToBranch(SILValue val, SILBasicBlock *dest,
|
|
TermInst *branch);
|
|
|
|
/// Get the linkage to be used for specializations of a function with
|
|
/// the given linkage.
|
|
SILLinkage getSpecializedLinkage(SILFunction *f, SILLinkage linkage);
|
|
|
|
/// Tries to perform jump-threading on all checked_cast_br instruction in
|
|
/// function \p Fn.
|
|
bool tryCheckedCastBrJumpThreading(
|
|
SILFunction *fn, DominanceInfo *dt,
|
|
SmallVectorImpl<SILBasicBlock *> &blocksForWorklist);
|
|
|
|
/// 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
|
|
/// \c recursivelyDeleteTriviallyDeadInstruction.
|
|
class InstructionDeleter {
|
|
private:
|
|
/// 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;
|
|
|
|
/// Callbacks used when adding/deleting instructions.
|
|
InstModCallbacks instModCallbacks;
|
|
|
|
public:
|
|
InstructionDeleter() : deadInstructions(), instModCallbacks() {}
|
|
InstructionDeleter(InstModCallbacks inputCallbacks)
|
|
: deadInstructions(), instModCallbacks(inputCallbacks) {}
|
|
|
|
/// If the instruction \p inst is dead, record it so that it can be cleaned
|
|
/// up.
|
|
void trackIfDead(SILInstruction *inst);
|
|
|
|
/// If the instruction \p inst is dead, delete it immediately and record
|
|
/// its operands so that they can be cleaned up later.
|
|
void deleteIfDead(SILInstruction *inst);
|
|
|
|
/// Delete the instruction \p inst and record instructions that may become
|
|
/// dead because of the removal of \c inst. This function will add necessary
|
|
/// ownership instructions to fix the lifetimes of the operands of \c inst to
|
|
/// compensate for its deletion. 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 function containing \c inst must be using ownership SIL.
|
|
/// \pre the instruction to be deleted must not have any use other than
|
|
/// incidental uses.
|
|
///
|
|
/// \p callback is called on each deleted instruction before deleting any
|
|
/// instructions. This way, the SIL is valid in the callback. However, the
|
|
/// callback cannot be used to update instruction iterators since other
|
|
/// instructions to be deleted remain in the instruction list.
|
|
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.
|
|
void forceDelete(SILInstruction *inst);
|
|
|
|
/// Force track this instruction as dead. Used to enable the deletion of a
|
|
/// bunch of instructions at the same time.
|
|
void forceTrackAsDead(SILInstruction *inst);
|
|
|
|
/// 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.
|
|
void cleanUpDeadInstructions();
|
|
|
|
/// Recursively visit users of \c inst (including \c inst)and delete
|
|
/// instructions that are dead (including \c inst).
|
|
void recursivelyDeleteUsersIfDead(SILInstruction *inst);
|
|
|
|
/// Recursively visit users of \c inst (including \c inst)and force delete
|
|
/// them. Also, destroy the consumed operands of the deleted instructions
|
|
/// whenever necessary.
|
|
void recursivelyForceDeleteUsersAndFixLifetimes(SILInstruction *inst);
|
|
|
|
private:
|
|
void deleteInstruction(SILInstruction *inst, bool fixOperandLifetimes);
|
|
};
|
|
|
|
/// 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
|
|
/// of trivial type.
|
|
/// If \p includeTrivialAddrArgs is true, also trivial address-type arguments
|
|
/// are included.
|
|
void getConsumedPartialApplyArgs(PartialApplyInst *pai,
|
|
SmallVectorImpl<Operand *> &argOperands,
|
|
bool includeTrivialAddrArgs);
|
|
|
|
/// Emit destroy operation for \p operand, and call appropriate functions from
|
|
/// \p callbacks for newly created instructions and deleted instructions.
|
|
void emitDestroyOperation(SILBuilder &builder, SILLocation loc,
|
|
SILValue operand, InstModCallbacks callbacks);
|
|
|
|
/// Collect all (transitive) users of \p inst which just copy or destroy \p
|
|
/// inst.
|
|
///
|
|
/// In other words: all users which do not prevent \p inst from being considered
|
|
/// as "dead".
|
|
/// Returns true, if there are no other users beside those collected in \p
|
|
/// destroys, i.e. if \p inst can be considered as "dead".
|
|
bool collectDestroys(SingleValueInstruction *inst,
|
|
SmallVectorImpl<SILInstruction *> &destroys);
|
|
|
|
/// If Closure is a partial_apply or thin_to_thick_function with only local
|
|
/// ref count users and a set of post-dominating releases:
|
|
///
|
|
/// 1. Remove all ref count operations and the closure.
|
|
/// 2. At each one of the last release locations insert releases for the
|
|
/// captured args if we have a partial_apply (except \p needKeepArgsAlive is
|
|
/// false).
|
|
///
|
|
/// In the future this should be extended to be less conservative with users.
|
|
bool tryDeleteDeadClosure(SingleValueInstruction *closure,
|
|
InstModCallbacks callbacks = InstModCallbacks(),
|
|
bool needKeepArgsAlive = true);
|
|
|
|
/// Given a SILValue argument to a partial apply \p Arg and the associated
|
|
/// parameter info for that argument, perform the necessary cleanups to Arg when
|
|
/// one is attempting to delete the partial apply.
|
|
void releasePartialApplyCapturedArg(
|
|
SILBuilder &builder, SILLocation loc, SILValue arg,
|
|
SILParameterInfo paramInfo,
|
|
InstModCallbacks callbacks = InstModCallbacks());
|
|
|
|
void deallocPartialApplyCapturedArg(
|
|
SILBuilder &builder, SILLocation loc, SILValue arg,
|
|
SILParameterInfo paramInfo);
|
|
|
|
/// Insert destroys of captured arguments of partial_apply [stack].
|
|
void insertDestroyOfCapturedArguments(
|
|
PartialApplyInst *pai, SILBuilder &builder,
|
|
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
|
|
[](SILValue arg) -> bool { return true; });
|
|
|
|
void insertDeallocOfCapturedArguments(
|
|
PartialApplyInst *pai, SILBuilder &builder);
|
|
|
|
/// This iterator 'looks through' one level of builtin expect users exposing all
|
|
/// users of the looked through builtin expect instruction i.e it presents a
|
|
/// view that shows all users as if there were no builtin expect instructions
|
|
/// interposed.
|
|
class IgnoreExpectUseIterator
|
|
: public std::iterator<std::forward_iterator_tag, Operand *, ptrdiff_t> {
|
|
ValueBaseUseIterator origUseChain;
|
|
ValueBaseUseIterator currentIter;
|
|
|
|
static BuiltinInst *isExpect(Operand *use) {
|
|
if (auto *bi = dyn_cast<BuiltinInst>(use->getUser()))
|
|
if (bi->getIntrinsicInfo().ID == llvm::Intrinsic::expect)
|
|
return bi;
|
|
return nullptr;
|
|
}
|
|
|
|
// Advance through expect users to their users until we encounter a user that
|
|
// is not an expect.
|
|
void advanceThroughExpects() {
|
|
while (currentIter == origUseChain
|
|
&& currentIter != ValueBaseUseIterator(nullptr)) {
|
|
auto *Expect = isExpect(*currentIter);
|
|
if (!Expect)
|
|
return;
|
|
currentIter = Expect->use_begin();
|
|
// Expect with no users advance to next item in original use chain.
|
|
if (currentIter == Expect->use_end())
|
|
currentIter = ++origUseChain;
|
|
}
|
|
}
|
|
|
|
public:
|
|
IgnoreExpectUseIterator(ValueBase *value)
|
|
: origUseChain(value->use_begin()), currentIter(value->use_begin()) {
|
|
advanceThroughExpects();
|
|
}
|
|
|
|
IgnoreExpectUseIterator() = default;
|
|
|
|
Operand *operator*() const { return *currentIter; }
|
|
Operand *operator->() const { return *currentIter; }
|
|
SILInstruction *getUser() const { return currentIter->getUser(); }
|
|
|
|
IgnoreExpectUseIterator &operator++() {
|
|
assert(**this && "increment past end()!");
|
|
if (origUseChain == currentIter) {
|
|
// Use chain of the original value.
|
|
++origUseChain;
|
|
++currentIter;
|
|
// Ignore expects.
|
|
advanceThroughExpects();
|
|
} else {
|
|
// Use chain of an expect.
|
|
++currentIter;
|
|
if (currentIter == ValueBaseUseIterator(nullptr)) {
|
|
// At the end of the use chain of an expect.
|
|
currentIter = ++origUseChain;
|
|
advanceThroughExpects();
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
IgnoreExpectUseIterator operator++(int unused) {
|
|
IgnoreExpectUseIterator copy = *this;
|
|
++*this;
|
|
return copy;
|
|
}
|
|
friend bool operator==(IgnoreExpectUseIterator lhs,
|
|
IgnoreExpectUseIterator rhs) {
|
|
return lhs.currentIter == rhs.currentIter;
|
|
}
|
|
friend bool operator!=(IgnoreExpectUseIterator lhs,
|
|
IgnoreExpectUseIterator rhs) {
|
|
return !(lhs == rhs);
|
|
}
|
|
};
|
|
|
|
inline iterator_range<IgnoreExpectUseIterator>
|
|
ignore_expect_uses(ValueBase *value) {
|
|
return make_range(IgnoreExpectUseIterator(value), IgnoreExpectUseIterator());
|
|
}
|
|
|
|
/// Run simplifyInstruction() on all of the instruction I's users if they only
|
|
/// have one result (since simplifyInstruction assumes that). Replace all uses
|
|
/// of the user with its simplification of we succeed. Returns true if we
|
|
/// succeed and false otherwise.
|
|
///
|
|
/// An example of how this is useful is in cases where one is splitting up an
|
|
/// aggregate and reforming it, the reformed aggregate may have extract
|
|
/// operations from it. These can be simplified and removed.
|
|
bool simplifyUsers(SingleValueInstruction *inst);
|
|
|
|
/// True if a type can be expanded
|
|
/// without a significant increase to code size.
|
|
bool shouldExpand(SILModule &module, SILType ty);
|
|
|
|
/// Check if the value of value is computed by means of a simple initialization.
|
|
/// Store the actual SILValue into \p Val and the reversed list of instructions
|
|
/// initializing it in \p Insns.
|
|
/// The check is performed by recursively walking the computation of the
|
|
/// SIL value being analyzed.
|
|
bool analyzeStaticInitializer(SILValue value,
|
|
SmallVectorImpl<SILInstruction *> &insns);
|
|
|
|
/// Returns true if the below operation will succeed.
|
|
bool canReplaceLoadSequence(SILInstruction *inst);
|
|
|
|
/// Replace load sequence which may contain
|
|
/// a chain of struct_element_addr followed by a load.
|
|
/// The sequence is traversed inside out, i.e.
|
|
/// starting with the innermost struct_element_addr
|
|
void replaceLoadSequence(SILInstruction *inst, SILValue value);
|
|
|
|
/// Do we have enough information to determine all callees that could
|
|
/// be reached by calling the function represented by Decl?
|
|
bool calleesAreStaticallyKnowable(SILModule &module, SILDeclRef decl);
|
|
|
|
/// Do we have enough information to determine all callees that could
|
|
/// be reached by calling the function represented by Decl?
|
|
bool calleesAreStaticallyKnowable(SILModule &module, ValueDecl *vd);
|
|
|
|
// Attempt to get the instance for , whose static type is the same as
|
|
// its exact dynamic type, returning a null SILValue() if we cannot find it.
|
|
// The information that a static type is the same as the exact dynamic,
|
|
// can be derived e.g.:
|
|
// - from a constructor or
|
|
// - from a successful outcome of a checked_cast_br [exact] instruction.
|
|
SILValue getInstanceWithExactDynamicType(SILValue instance,
|
|
ClassHierarchyAnalysis *cha);
|
|
|
|
/// Try to determine the exact dynamic type of an object.
|
|
/// returns the exact dynamic type of the object, or an empty type if the exact
|
|
/// type could not be determined.
|
|
SILType getExactDynamicType(SILValue instance, ClassHierarchyAnalysis *cha,
|
|
bool forUnderlyingObject = false);
|
|
|
|
/// Try to statically determine the exact dynamic type of the underlying object.
|
|
/// returns the exact dynamic type of the underlying object, or an empty SILType
|
|
/// if the exact type could not be determined.
|
|
SILType getExactDynamicTypeOfUnderlyingObject(SILValue instance,
|
|
ClassHierarchyAnalysis *cha);
|
|
|
|
// Move only data structure that is the result of findLocalApplySite.
|
|
///
|
|
/// NOTE: Generally it is not suggested to have move only types that contain
|
|
/// small vectors. Since our small vectors contain one element or a std::vector
|
|
/// like data structure , this is ok since we will either just copy the single
|
|
/// element when we do the move or perform a move of the vector type.
|
|
struct LLVM_LIBRARY_VISIBILITY FindLocalApplySitesResult {
|
|
/// Contains the list of local non fully applied partial apply sites that we
|
|
/// found.
|
|
SmallVector<ApplySite, 1> partialApplySites;
|
|
|
|
/// Contains the list of full apply sites that we found.
|
|
SmallVector<FullApplySite, 1> fullApplySites;
|
|
|
|
/// Set to true if the function_ref escapes into a use that our analysis does
|
|
/// not understand. Set to false if we found a use that had an actual
|
|
/// escape. Set to None if we did not find any call sites, but also didn't
|
|
/// find any "escaping uses" as well.
|
|
///
|
|
/// The none case is so that we can distinguish in between saying that a value
|
|
/// did escape and saying that we did not find any conservative information.
|
|
bool escapes;
|
|
|
|
FindLocalApplySitesResult() = default;
|
|
FindLocalApplySitesResult(const FindLocalApplySitesResult &) = delete;
|
|
FindLocalApplySitesResult &
|
|
operator=(const FindLocalApplySitesResult &) = delete;
|
|
FindLocalApplySitesResult(FindLocalApplySitesResult &&) = default;
|
|
FindLocalApplySitesResult &operator=(FindLocalApplySitesResult &&) = default;
|
|
~FindLocalApplySitesResult() = default;
|
|
|
|
/// Treat this function ref as escaping only if we found an actual user we
|
|
/// didn't understand. Do not treat it as escaping if we did not find any
|
|
/// users at all.
|
|
bool isEscaping() const { return escapes; }
|
|
};
|
|
|
|
/// Returns .some(FindLocalApplySitesResult) if we found any interesting
|
|
/// information for the given function_ref. Otherwise, returns None.
|
|
///
|
|
/// We consider "interesting information" to mean inclusively that:
|
|
///
|
|
/// 1. We discovered that the function_ref never escapes.
|
|
/// 2. We were able to find either a partial apply or a full apply site.
|
|
Optional<FindLocalApplySitesResult>
|
|
findLocalApplySites(FunctionRefBaseInst *fri);
|
|
|
|
/// Gets the base implementation of a method.
|
|
AbstractFunctionDecl *getBaseMethod(AbstractFunctionDecl *FD);
|
|
|
|
bool tryOptimizeApplyOfPartialApply(
|
|
PartialApplyInst *pai, SILBuilderContext &builderCtxt,
|
|
InstModCallbacks callbacks = InstModCallbacks());
|
|
|
|
/// Clone this full apply site, replacing the callee with \p newCallee while
|
|
/// doing so.
|
|
///
|
|
/// The current full apply site is used as an insertion point, so the caller
|
|
/// must clean up this full apply site.
|
|
FullApplySite cloneFullApplySiteReplacingCallee(FullApplySite applySite,
|
|
SILValue newCallee,
|
|
SILBuilderContext &builderCtx);
|
|
|
|
/// This is a low level routine that makes all uses of \p svi uses of \p
|
|
/// newValue (ignoring end scope markers) and then deletes \p svi and all end
|
|
/// scope markers. Then returns the next inst to process.
|
|
SILBasicBlock::iterator replaceAllUsesAndErase(SingleValueInstruction *svi,
|
|
SILValue newValue,
|
|
InstModCallbacks &callbacks);
|
|
|
|
/// This API is equivalent to performing \p use->set(\p newValue) except that:
|
|
///
|
|
/// 1. If the user of \p use is an end scope, this API no-opts. This API is only
|
|
/// used in contexts where we are rewriting uses and are not interesting in
|
|
/// end scope instructions since we are moving uses from one scope to another
|
|
/// scope.
|
|
///
|
|
/// 2. If the user of \p use is not an end scope, but is a lifetime ending use
|
|
/// of \p use->get(), we insert a destroy_value|end_borrow as appropriate on
|
|
/// \p use->get() to ensure \p use->get()'s lifetime is still ended. We
|
|
/// assume that if \p use->getUser() is lifetime ending, that our caller has
|
|
/// ensured that we can end \p newValue's lifetime.
|
|
SILBasicBlock::iterator replaceSingleUse(Operand *use, SILValue newValue,
|
|
InstModCallbacks &callbacks);
|
|
|
|
/// Creates a copy of \p value and inserts additional control equivalent copy
|
|
/// and destroy at leaking blocks to adjust ownership and make available for use
|
|
/// at \p inBlock.
|
|
SILValue
|
|
makeCopiedValueAvailable(SILValue value, SILBasicBlock *inBlock);
|
|
|
|
/// Given a newly created @owned value \p value without any uses, this utility
|
|
/// inserts control equivalent copy and destroy at leaking blocks to adjust
|
|
/// ownership and make \p value available for use at \p inBlock.
|
|
///
|
|
/// inBlock must be the only point at which \p value will be consumed. If this
|
|
/// consuming point is within a loop, this will create and return a copy of \p
|
|
/// value inside \p inBlock.
|
|
SILValue
|
|
makeNewValueAvailable(SILValue value, SILBasicBlock *inBlock);
|
|
|
|
/// Given an ssa value \p value, create destroy_values at leaking blocks
|
|
///
|
|
/// Warning: This does not properly cleanup an OSSA lifetime with a consuming
|
|
/// use blocks inside a loop relative to \p value. The client must create
|
|
/// separate copies for any uses within the loop.
|
|
void endLifetimeAtLeakingBlocks(SILValue value,
|
|
ArrayRef<SILBasicBlock *> userBBs);
|
|
|
|
/// Given a forwarding instruction, eliminate it if all of its users are debug
|
|
/// instructions and ownership uses.
|
|
bool tryEliminateOnlyOwnershipUsedForwardingInst(
|
|
SingleValueInstruction *forwardingInst, InstModCallbacks &callbacks);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|