mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This utility is generally a horrible idea but even worse the callers were not doing anything to ensure the required invariants actually held. Add a new canReplaceLoadSequence() method and chek it in the right places.
667 lines
26 KiB
C++
667 lines
26 KiB
C++
//===--- Local.h - Local SIL transformations. -------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
|
|
#define SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
|
|
|
|
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILCloner.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include <functional>
|
|
#include <utility>
|
|
|
|
namespace swift {
|
|
|
|
class DominanceInfo;
|
|
template <class T> class NullablePtr;
|
|
|
|
/// Transform a Use Range (Operand*) into a User Range (SILInstruction*)
|
|
using UserTransform = std::function<SILInstruction *(Operand *)>;
|
|
using ValueBaseUserRange =
|
|
TransformRange<IteratorRange<ValueBase::use_iterator>, UserTransform>;
|
|
|
|
inline ValueBaseUserRange makeUserRange(
|
|
iterator_range<ValueBase::use_iterator> R) {
|
|
auto toUser = [](Operand *O) { return O->getUser(); };
|
|
return makeTransformRange(makeIteratorRange(R.begin(), R.end()),
|
|
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);
|
|
|
|
/// For each of the given instructions, if they are dead delete them
|
|
/// along with their dead operands.
|
|
///
|
|
/// \param I 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 C a callback called whenever an instruction is deleted.
|
|
void
|
|
recursivelyDeleteTriviallyDeadInstructions(
|
|
ArrayRef<SILInstruction*> I, bool Force = false,
|
|
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
|
|
|
|
/// For each of the given instructions, if they are dead delete them
|
|
/// along with their dead operands.
|
|
///
|
|
/// \param I The ArrayRef of instructions to be deleted.
|
|
/// \param InstIter is updated to the next valid instruction if it points to any
|
|
/// deleted instruction, including debug values.
|
|
/// \param Force If Force is set, don't check if the top level instructions
|
|
/// are considered dead - delete them regardless.
|
|
/// \param C a callback called whenever an instruction is deleted.
|
|
void recursivelyDeleteTriviallyDeadInstructions(
|
|
ArrayRef<SILInstruction *> I, SILBasicBlock::iterator &InstIter,
|
|
bool Force = false,
|
|
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
|
|
|
|
/// If the given instruction is dead, delete it along with its dead
|
|
/// operands.
|
|
///
|
|
/// \param I 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 C a callback called whenever an instruction is deleted.
|
|
///
|
|
/// Returns a valid instruction iterator to the next nondeleted instruction
|
|
/// after `I`.
|
|
SILBasicBlock::iterator recursivelyDeleteTriviallyDeadInstructions(
|
|
SILInstruction *I, bool Force = false,
|
|
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
|
|
|
|
/// 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 *I);
|
|
|
|
/// Return true if this is a release instruction that's not going to
|
|
/// free the object.
|
|
bool isIntermediateRelease(SILInstruction *I, 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,
|
|
llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
|
|
|
|
/// Recursively erase all of the uses of the value (but not the
|
|
/// value itself)
|
|
void eraseUsesOfValue(SILValue V);
|
|
|
|
FullApplySite findApplyFromDevirtualizedResult(SILValue value);
|
|
|
|
/// 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.
|
|
SILValue castValueToABICompatibleType(SILBuilder *B, SILLocation Loc,
|
|
SILValue Value,
|
|
SILType SrcTy,
|
|
SILType DestTy);
|
|
/// 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);
|
|
|
|
/// Handle the mechanical aspects of removing an unreachable block.
|
|
void removeDeadBlock(SILBasicBlock *BB);
|
|
|
|
/// Remove all instructions in the body of \p BB in safe manner by using
|
|
/// undef.
|
|
void clearBlockBody(SILBasicBlock *BB);
|
|
|
|
/// Get the linkage to be used for specializations of a function with
|
|
/// the given linkage.
|
|
SILLinkage getSpecializedLinkage(SILFunction *F, SILLinkage L);
|
|
|
|
/// Tries to optimize a given apply instruction if it is a concatenation of
|
|
/// string literals. Returns a new instruction if optimization was possible.
|
|
SingleValueInstruction *tryToConcatenateStrings(ApplyInst *AI, SILBuilder &B);
|
|
|
|
/// 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 structure containing callbacks that are called when an instruction is
|
|
/// removed or added.
|
|
struct InstModCallbacks {
|
|
using CallbackTy = std::function<void (SILInstruction *)>;
|
|
CallbackTy DeleteInst = [](SILInstruction *I) {
|
|
I->eraseFromParent();
|
|
};
|
|
CallbackTy CreatedNewInst = [](SILInstruction *){};
|
|
|
|
InstModCallbacks(CallbackTy DeleteInst, CallbackTy CreatedNewInst)
|
|
: DeleteInst(DeleteInst), CreatedNewInst(CreatedNewInst) {}
|
|
InstModCallbacks() = default;
|
|
~InstModCallbacks() = default;
|
|
InstModCallbacks(const InstModCallbacks &) = default;
|
|
InstModCallbacks(InstModCallbacks &&) = default;
|
|
};
|
|
|
|
/// 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. Add each one of the last release locations insert releases for the
|
|
/// captured args if we have a partial_apply.
|
|
///
|
|
/// In the future this should be extended to be less conservative with users.
|
|
bool
|
|
tryDeleteDeadClosure(SingleValueInstruction *Closure,
|
|
InstModCallbacks Callbacks = InstModCallbacks());
|
|
|
|
/// 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 PInfo,
|
|
InstModCallbacks Callbacks = InstModCallbacks());
|
|
|
|
/// Insert destroys of captured arguments of partial_apply [stack].
|
|
void insertDestroyOfCapturedArguments(
|
|
PartialApplyInst *PAI, SILBuilder &B,
|
|
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
|
|
[](SILValue arg) -> bool { return true; });
|
|
|
|
/// This computes the lifetime of a single SILValue.
|
|
///
|
|
/// This does not compute a set of jointly postdominating use points. Instead it
|
|
/// assumes that the value's existing uses already jointly postdominate the
|
|
/// definition. This makes sense for values that are returned +1 from an
|
|
/// instruction, like partial_apply, and therefore must be released on all paths
|
|
/// via strong_release or apply.
|
|
class ValueLifetimeAnalysis {
|
|
public:
|
|
|
|
/// The lifetime frontier for the value. It is the list of instructions
|
|
/// following the last uses of the value. All the frontier instructions
|
|
/// end the value's lifetime.
|
|
typedef llvm::SmallVector<SILInstruction *, 4> Frontier;
|
|
|
|
/// Constructor for the value \p Def with a specific set of users of Def's
|
|
/// users.
|
|
ValueLifetimeAnalysis(SILInstruction *Def, ArrayRef<SILInstruction*> UserList) :
|
|
DefValue(Def), UserSet(UserList.begin(), UserList.end()) {
|
|
propagateLiveness();
|
|
}
|
|
|
|
/// Constructor for the value \p Def considering all the value's uses.
|
|
ValueLifetimeAnalysis(SILInstruction *Def) : DefValue(Def) {
|
|
for (auto result : Def->getResults()) {
|
|
for (Operand *op : result->getUses()) {
|
|
UserSet.insert(op->getUser());
|
|
}
|
|
}
|
|
propagateLiveness();
|
|
}
|
|
|
|
enum Mode {
|
|
/// Don't split critical edges if the frontier instructions are located on
|
|
/// a critical edges. Instead fail.
|
|
DontModifyCFG,
|
|
|
|
/// Split critical edges if the frontier instructions are located on
|
|
/// a critical edges.
|
|
AllowToModifyCFG,
|
|
|
|
/// Require that all users must commonly post-dominate the definition. In
|
|
/// other words: All paths from the definition to the function exit must
|
|
/// contain at least one use. Fail if this is not the case.
|
|
UsersMustPostDomDef
|
|
};
|
|
|
|
/// Computes and returns the lifetime frontier for the value in \p Fr.
|
|
///
|
|
/// Returns true if all instructions in the frontier could be found in
|
|
/// non-critical edges.
|
|
/// Returns false if some frontier instructions are located on critical edges.
|
|
/// In this case, if \p mode is AllowToModifyCFG, those critical edges are
|
|
/// split, otherwise nothing is done and the returned \p Fr is not valid.
|
|
///
|
|
/// If \p deadEndBlocks is provided, all dead-end blocks are ignored. This
|
|
/// prevents unreachable-blocks to be included in the frontier.
|
|
bool computeFrontier(Frontier &Fr, Mode mode,
|
|
DeadEndBlocks *DEBlocks = nullptr);
|
|
|
|
/// Returns true if the instruction \p Inst is located within the value's
|
|
/// lifetime.
|
|
/// It is assumed that \p Inst is located after the value's definition.
|
|
bool isWithinLifetime(SILInstruction *Inst);
|
|
|
|
/// Returns true if the value is alive at the begin of block \p BB.
|
|
bool isAliveAtBeginOfBlock(SILBasicBlock *BB) {
|
|
return LiveBlocks.count(BB) && BB != DefValue->getParent();
|
|
}
|
|
|
|
/// Checks if there is a dealloc_ref inside the value's live range.
|
|
bool containsDeallocRef(const Frontier &Frontier);
|
|
|
|
/// For debug dumping.
|
|
void dump() const;
|
|
|
|
private:
|
|
|
|
/// The value.
|
|
SILInstruction *DefValue;
|
|
|
|
/// The set of blocks where the value is live.
|
|
llvm::SmallSetVector<SILBasicBlock *, 16> LiveBlocks;
|
|
|
|
/// The set of instructions where the value is used, or the users-list
|
|
/// provided with the constructor.
|
|
llvm::SmallPtrSet<SILInstruction*, 16> UserSet;
|
|
|
|
/// Propagates the liveness information up the control flow graph.
|
|
void propagateLiveness();
|
|
|
|
/// Returns the last use of the value in the live block \p BB.
|
|
SILInstruction *findLastUserInBlock(SILBasicBlock *BB);
|
|
};
|
|
|
|
/// Clone a single basic block and any required successor edges within the same
|
|
/// function.
|
|
class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
|
|
using SuperTy = SILCloner<BasicBlockCloner>;
|
|
friend class SILCloner<BasicBlockCloner>;
|
|
|
|
protected:
|
|
/// The original block to be cloned.
|
|
SILBasicBlock *origBB;
|
|
|
|
public:
|
|
/// An ordered list of old to new available value pairs.
|
|
///
|
|
/// updateSSAAfterCloning() expects this public field to hold values that may
|
|
/// be remapped in the cloned block and live out.
|
|
SmallVector<std::pair<SILValue, SILValue>, 16> AvailVals;
|
|
|
|
// Clone blocks starting at `origBB`, within the same function.
|
|
BasicBlockCloner(SILBasicBlock *origBB)
|
|
: SILCloner(*origBB->getParent()), origBB(origBB) {}
|
|
|
|
void cloneBlock(SILBasicBlock *insertAfterBB = nullptr) {
|
|
SmallVector<SILBasicBlock *, 4> successorBBs;
|
|
successorBBs.reserve(origBB->getSuccessors().size());
|
|
llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs));
|
|
cloneReachableBlocks(origBB, successorBBs, insertAfterBB);
|
|
}
|
|
|
|
/// Clone the given branch instruction's destination block, splitting
|
|
/// its successors, and rewrite the branch instruction.
|
|
void cloneBranchTarget(BranchInst *BI) {
|
|
assert(origBB == BI->getDestBB());
|
|
|
|
cloneBlock(/*insertAfter*/BI->getParent());
|
|
|
|
SILBuilderWithScope(BI).createBranch(BI->getLoc(), getNewBB(),
|
|
BI->getArgs());
|
|
BI->eraseFromParent();
|
|
}
|
|
|
|
/// Get the newly cloned block corresponding to `origBB`.
|
|
SILBasicBlock *getNewBB() {
|
|
return remapBasicBlock(origBB);
|
|
}
|
|
|
|
/// Call this after processing all instructions to fix the control flow
|
|
/// graph. The branch cloner may have left critical edges.
|
|
bool splitCriticalEdges(DominanceInfo *DT, SILLoopInfo *LI);
|
|
|
|
protected:
|
|
// MARK: CRTP overrides.
|
|
|
|
/// Override getMappedValue to allow values defined outside the block to be
|
|
/// cloned to be reused in the newly cloned block.
|
|
SILValue getMappedValue(SILValue Value) {
|
|
if (auto SI = Value->getDefiningInstruction()) {
|
|
if (!isBlockCloned(SI->getParent()))
|
|
return Value;
|
|
} else if (auto BBArg = dyn_cast<SILArgument>(Value)) {
|
|
if (!isBlockCloned(BBArg->getParent()))
|
|
return Value;
|
|
} else {
|
|
assert(isa<SILUndef>(Value) && "Unexpected Value kind");
|
|
return Value;
|
|
}
|
|
// `value` is not defined outside the cloned block, so consult the cloner's
|
|
// map of cloned values.
|
|
return SuperTy::getMappedValue(Value);
|
|
}
|
|
|
|
void mapValue(SILValue origValue, SILValue mappedValue) {
|
|
SuperTy::mapValue(origValue, mappedValue);
|
|
AvailVals.emplace_back(origValue, mappedValue);
|
|
}
|
|
};
|
|
|
|
/// Helper function to perform SSA updates in case of jump threading.
|
|
void updateSSAAfterCloning(BasicBlockCloner &Cloner, SILBasicBlock *SrcBB,
|
|
SILBasicBlock *DestBB);
|
|
|
|
// Helper class that provides a callback that can be used in
|
|
// inliners/cloners for collecting new call sites.
|
|
class CloneCollector {
|
|
public:
|
|
typedef std::pair<SILInstruction *, SILInstruction *> value_type;
|
|
typedef std::function<void(SILInstruction *, SILInstruction *)> CallbackType;
|
|
typedef std::function<bool (SILInstruction *)> FilterType;
|
|
|
|
private:
|
|
FilterType Filter;
|
|
|
|
// Pairs of collected instructions; (new, old)
|
|
llvm::SmallVector<value_type, 4> InstructionPairs;
|
|
|
|
void collect(SILInstruction *Old, SILInstruction *New) {
|
|
if (Filter(New))
|
|
InstructionPairs.push_back(std::make_pair(New, Old));
|
|
}
|
|
|
|
public:
|
|
CloneCollector(FilterType Filter) : Filter(Filter) {}
|
|
|
|
CallbackType getCallback() {
|
|
return std::bind(&CloneCollector::collect, this, std::placeholders::_1,
|
|
std::placeholders::_2);
|
|
}
|
|
|
|
llvm::SmallVectorImpl<value_type> &getInstructionPairs() {
|
|
return InstructionPairs;
|
|
}
|
|
};
|
|
|
|
/// 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 *V)
|
|
: OrigUseChain(V->use_begin()), CurrentIter(V->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 *V) {
|
|
return make_range(IgnoreExpectUseIterator(V),
|
|
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 *I);
|
|
|
|
/// True if a type can be expanded
|
|
/// without a significant increase to code size.
|
|
bool shouldExpand(SILModule &Module, SILType Ty);
|
|
|
|
/// Check if a given type is a simple type, i.e. a builtin
|
|
/// integer or floating point type or a struct/tuple whose members
|
|
/// are of simple types.
|
|
bool isSimpleType(SILType SILTy, SILModule& Module);
|
|
|
|
/// Check if the value of V 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 V,
|
|
SmallVectorImpl<SILInstruction *> &Insns);
|
|
|
|
/// Returns true if the below operation will succeed.
|
|
bool canReplaceLoadSequence(SILInstruction *I);
|
|
|
|
/// 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 *I,
|
|
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 &M, SILDeclRef Decl);
|
|
|
|
// Attempt to get the instance for S, 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 S, SILModule &M,
|
|
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 S, SILModule &M,
|
|
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 S, SILModule &M,
|
|
ClassHierarchyAnalysis *CHA);
|
|
|
|
/// Utility class for cloning init values into the static initializer of a
|
|
/// SILGlobalVariable.
|
|
class StaticInitCloner : public SILCloner<StaticInitCloner> {
|
|
friend class SILInstructionVisitor<StaticInitCloner>;
|
|
friend class SILCloner<StaticInitCloner>;
|
|
|
|
/// The number of not yet cloned operands for each instruction.
|
|
llvm::DenseMap<SILInstruction *, int> NumOpsToClone;
|
|
|
|
/// List of instructions for which all operands are already cloned (or which
|
|
/// don't have any operands).
|
|
llvm::SmallVector<SILInstruction *, 8> ReadyToClone;
|
|
|
|
public:
|
|
StaticInitCloner(SILGlobalVariable *GVar)
|
|
: SILCloner<StaticInitCloner>(GVar) { }
|
|
|
|
/// Add \p InitVal and all its operands (transitively) for cloning.
|
|
///
|
|
/// Note: all init values must are added, before calling clone().
|
|
void add(SILInstruction *InitVal);
|
|
|
|
/// Clone \p InitVal and all its operands into the initializer of the
|
|
/// SILGlobalVariable.
|
|
///
|
|
/// \return Returns the cloned instruction in the SILGlobalVariable.
|
|
SingleValueInstruction *clone(SingleValueInstruction *InitVal);
|
|
|
|
/// Convenience function to clone a single \p InitVal.
|
|
static void appendToInitializer(SILGlobalVariable *GVar,
|
|
SingleValueInstruction *InitVal) {
|
|
StaticInitCloner Cloner(GVar);
|
|
Cloner.add(InitVal);
|
|
Cloner.clone(InitVal);
|
|
}
|
|
|
|
protected:
|
|
SILLocation remapLocation(SILLocation Loc) {
|
|
return ArtificialUnreachableLocation();
|
|
}
|
|
};
|
|
|
|
/// 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);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|