blob: 8021602717f38ef1e5cbf854d2bb70247793d562 [file] [log] [blame]
//===--- CSE.cpp - Simple and fast CSE pass -------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This pass performs a simple dominator tree walk that eliminates trivially
// redundant instructions.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-cse"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/Dominance.h"
#include "swift/SIL/SILCloner.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILOpenedArchetypesTracker.h"
#include "swift/SIL/SILType.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/RecyclingAllocator.h"
STATISTIC(NumOpenExtRemoved,
"Number of open_existential_addr instructions removed");
STATISTIC(NumSimplify, "Number of instructions simplified or DCE'd");
STATISTIC(NumCSE, "Number of instructions CSE'd");
using namespace swift;
//===----------------------------------------------------------------------===//
// Simple Value
//===----------------------------------------------------------------------===//
namespace {
/// SimpleValue - Instances of this struct represent available values in the
/// scoped hash table.
struct SimpleValue {
SILInstruction *Inst;
SimpleValue(SILInstruction *I) : Inst(I) { }
bool isSentinel() const {
return Inst == llvm::DenseMapInfo<SILInstruction *>::getEmptyKey() ||
Inst == llvm::DenseMapInfo<SILInstruction *>::getTombstoneKey();
}
};
} // end anonymous namespace
namespace llvm {
template <> struct DenseMapInfo<SimpleValue> {
static inline SimpleValue getEmptyKey() {
return DenseMapInfo<SILInstruction *>::getEmptyKey();
}
static inline SimpleValue getTombstoneKey() {
return DenseMapInfo<SILInstruction *>::getTombstoneKey();
}
static unsigned getHashValue(SimpleValue Val);
static bool isEqual(SimpleValue LHS, SimpleValue RHS);
};
} // end namespace llvm
namespace {
class HashVisitor : public SILInstructionVisitor<HashVisitor, llvm::hash_code> {
using hash_code = llvm::hash_code;
public:
hash_code visitSILInstruction(SILInstruction *) {
llvm_unreachable("No hash implemented for the given type");
}
hash_code visitBridgeObjectToRefInst(BridgeObjectToRefInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitBridgeObjectToWordInst(BridgeObjectToWordInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitClassifyBridgeObjectInst(ClassifyBridgeObjectInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
hash_code visitValueToBridgeObjectInst(ValueToBridgeObjectInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
hash_code visitRefToBridgeObjectInst(RefToBridgeObjectInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(
X->getKind(), X->getType(),
llvm::hash_combine_range(Operands.begin(), Operands.end()));
}
hash_code visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitUncheckedAddrCastInst(UncheckedAddrCastInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitFunctionRefInst(FunctionRefInst *X) {
return llvm::hash_combine(X->getKind(),
X->getInitiallyReferencedFunction());
}
hash_code visitGlobalAddrInst(GlobalAddrInst *X) {
return llvm::hash_combine(X->getKind(), X->getReferencedGlobal());
}
hash_code visitIntegerLiteralInst(IntegerLiteralInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getValue());
}
hash_code visitFloatLiteralInst(FloatLiteralInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getBits());
}
hash_code visitRefElementAddrInst(RefElementAddrInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getField());
}
hash_code visitRefTailAddrInst(RefTailAddrInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
hash_code visitProjectBoxInst(ProjectBoxInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
hash_code visitRefToRawPointerInst(RefToRawPointerInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
hash_code visitRawPointerToRefInst(RawPointerToRefInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
#define LOADABLE_REF_STORAGE(Name, ...) \
hash_code visit##Name##ToRefInst(Name##ToRefInst *X) { \
return llvm::hash_combine(X->getKind(), X->getOperand()); \
} \
hash_code visitRefTo##Name##Inst(RefTo##Name##Inst *X) { \
return llvm::hash_combine(X->getKind(), X->getOperand()); \
}
#include "swift/AST/ReferenceStorage.def"
hash_code visitUpcastInst(UpcastInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitStringLiteralInst(StringLiteralInst *X) {
return llvm::hash_combine(X->getKind(), X->getEncoding(), X->getValue());
}
hash_code visitStructInst(StructInst *X) {
// This is safe since we are hashing the operands using the actual pointer
// values of the values being used by the operand.
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(X->getKind(), X->getStructDecl(),
llvm::hash_combine_range(Operands.begin(), Operands.end()));
}
hash_code visitStructExtractInst(StructExtractInst *X) {
return llvm::hash_combine(X->getKind(), X->getStructDecl(), X->getField(),
X->getOperand());
}
hash_code visitStructElementAddrInst(StructElementAddrInst *X) {
return llvm::hash_combine(X->getKind(), X->getStructDecl(), X->getField(),
X->getOperand());
}
hash_code visitCondFailInst(CondFailInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand());
}
hash_code visitClassMethodInst(ClassMethodInst *X) {
return llvm::hash_combine(X->getKind(),
X->getType(),
X->getOperand());
}
hash_code visitSuperMethodInst(SuperMethodInst *X) {
return llvm::hash_combine(X->getKind(),
X->getType(),
X->getOperand());
}
hash_code visitTupleInst(TupleInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(X->getKind(), X->getTupleType(),
llvm::hash_combine_range(Operands.begin(), Operands.end()));
}
hash_code visitTupleExtractInst(TupleExtractInst *X) {
return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldNo(),
X->getOperand());
}
hash_code visitTupleElementAddrInst(TupleElementAddrInst *X) {
return llvm::hash_combine(X->getKind(), X->getTupleType(), X->getFieldNo(),
X->getOperand());
}
hash_code visitMetatypeInst(MetatypeInst *X) {
return llvm::hash_combine(X->getKind(), X->getType());
}
hash_code visitValueMetatypeInst(ValueMetatypeInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitExistentialMetatypeInst(ExistentialMetatypeInst *X) {
return llvm::hash_combine(X->getKind(), X->getType());
}
hash_code visitObjCProtocolInst(ObjCProtocolInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getProtocol());
}
hash_code visitIndexRawPointerInst(IndexRawPointerInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getBase(),
X->getIndex());
}
hash_code visitPointerToAddressInst(PointerToAddressInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand(),
X->isStrict());
}
hash_code visitAddressToPointerInst(AddressToPointerInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getOperand());
}
hash_code visitApplyInst(ApplyInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(X->getKind(), X->getCallee(),
llvm::hash_combine_range(Operands.begin(),
Operands.end()),
X->hasSubstitutions());
}
hash_code visitBuiltinInst(BuiltinInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(X->getKind(), X->getName().get(),
llvm::hash_combine_range(Operands.begin(),
Operands.end()),
X->hasSubstitutions());
}
hash_code visitEnumInst(EnumInst *X) {
// We hash the enum by hashing its kind, element, and operand if it has one.
if (!X->hasOperand())
return llvm::hash_combine(X->getKind(), X->getElement());
return llvm::hash_combine(X->getKind(), X->getElement(), X->getOperand());
}
hash_code visitUncheckedEnumDataInst(UncheckedEnumDataInst *X) {
// We hash the enum by hashing its kind, element, and operand.
return llvm::hash_combine(X->getKind(), X->getElement(), X->getOperand());
}
hash_code visitIndexAddrInst(IndexAddrInst *X) {
return llvm::hash_combine(X->getKind(), X->getType(), X->getBase(),
X->getIndex());
}
hash_code visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitObjCMetatypeToObjectInst(ObjCMetatypeToObjectInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitObjCExistentialMetatypeToObjectInst(
ObjCExistentialMetatypeToObjectInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitUncheckedRefCastInst(UncheckedRefCastInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitSelectEnumInstBase(SelectEnumInstBase *X) {
auto hash = llvm::hash_combine(X->getKind(),
X->getEnumOperand(),
X->getType(),
X->hasDefault());
for (unsigned i = 0, e = X->getNumCases(); i < e; ++i) {
hash = llvm::hash_combine(hash, X->getCase(i).first,
X->getCase(i).second);
}
if (X->hasDefault())
hash = llvm::hash_combine(hash, X->getDefaultResult());
return hash;
}
hash_code visitSelectEnumInst(SelectEnumInst *X) {
return visitSelectEnumInstBase(X);
}
hash_code visitSelectEnumAddrInst(SelectEnumAddrInst *X) {
return visitSelectEnumInstBase(X);
}
hash_code visitSelectValueInst(SelectValueInst *X) {
auto hash = llvm::hash_combine(X->getKind(),
X->getOperand(),
X->getType(),
X->hasDefault());
for (unsigned i = 0, e = X->getNumCases(); i < e; ++i) {
hash = llvm::hash_combine(hash, X->getCase(i).first,
X->getCase(i).second);
}
if (X->hasDefault())
hash = llvm::hash_combine(hash, X->getDefaultResult());
return hash;
}
hash_code visitThinFunctionToPointerInst(ThinFunctionToPointerInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitPointerToThinFunctionInst(PointerToThinFunctionInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());
}
hash_code visitWitnessMethodInst(WitnessMethodInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(X->getKind(),
X->getLookupType().getPointer(),
X->getMember().getHashCode(),
X->getConformance(),
X->getType(),
!X->getTypeDependentOperands().empty(),
llvm::hash_combine_range(
Operands.begin(),
Operands.end()));
}
hash_code visitMarkDependenceInst(MarkDependenceInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(
X->getKind(), X->getType(),
llvm::hash_combine_range(Operands.begin(), Operands.end()));
}
hash_code visitOpenExistentialRefInst(OpenExistentialRefInst *X) {
auto ArchetypeTy = X->getType().castTo<ArchetypeType>();
auto ConformsTo = ArchetypeTy->getConformsTo();
return llvm::hash_combine(
X->getKind(), X->getOperand(),
llvm::hash_combine_range(ConformsTo.begin(), ConformsTo.end()));
}
};
} // end anonymous namespace
unsigned llvm::DenseMapInfo<SimpleValue>::getHashValue(SimpleValue Val) {
return HashVisitor().visit(Val.Inst);
}
bool llvm::DenseMapInfo<SimpleValue>::isEqual(SimpleValue LHS,
SimpleValue RHS) {
SILInstruction *LHSI = LHS.Inst, *RHSI = RHS.Inst;
if (LHS.isSentinel() || RHS.isSentinel())
return LHSI == RHSI;
auto LOpen = dyn_cast<OpenExistentialRefInst>(LHSI);
auto ROpen = dyn_cast<OpenExistentialRefInst>(RHSI);
if (LOpen && ROpen) {
// Check operands.
if (LOpen->getOperand() != ROpen->getOperand())
return false;
// Consider the types of two open_existential_ref instructions to be equal,
// if the sets of protocols they conform to are equal ...
auto LHSArchetypeTy = LOpen->getType().castTo<ArchetypeType>();
auto RHSArchetypeTy = ROpen->getType().castTo<ArchetypeType>();
auto LHSConformsTo = LHSArchetypeTy->getConformsTo();
auto RHSConformsTo = RHSArchetypeTy->getConformsTo();
if (LHSConformsTo != RHSConformsTo)
return false;
// ... and other constraints are equal.
if (LHSArchetypeTy->requiresClass() != RHSArchetypeTy->requiresClass())
return false;
if (LHSArchetypeTy->getSuperclass().getPointer() !=
RHSArchetypeTy->getSuperclass().getPointer())
return false;
if (LHSArchetypeTy->getLayoutConstraint() !=
RHSArchetypeTy->getLayoutConstraint())
return false;
return true;
}
return LHSI->getKind() == RHSI->getKind() && LHSI->isIdenticalTo(RHSI);
}
//===----------------------------------------------------------------------===//
// CSE Interface
//===----------------------------------------------------------------------===//
namespace swift {
/// CSE - This pass does a simple depth-first walk over the dominator tree,
/// eliminating trivially redundant instructions and using simplifyInstruction
/// to canonicalize things as it goes. It is intended to be fast and catch
/// obvious cases so that SILCombine and other passes are more effective.
class CSE {
public:
typedef llvm::ScopedHashTableVal<SimpleValue, ValueBase *> SimpleValueHTType;
typedef llvm::RecyclingAllocator<llvm::BumpPtrAllocator, SimpleValueHTType>
AllocatorTy;
typedef llvm::ScopedHashTable<SimpleValue, SILInstruction *,
llvm::DenseMapInfo<SimpleValue>,
AllocatorTy> ScopedHTType;
/// AvailableValues - This scoped hash table contains the current values of
/// all of our simple scalar expressions. As we walk down the domtree, we
/// look to see if instructions are in this: if so, we replace them with what
/// we find, otherwise we insert them so that dominated values can succeed in
/// their lookup.
ScopedHTType *AvailableValues;
SideEffectAnalysis *SEA;
CSE(bool RunsOnHighLevelSil, SideEffectAnalysis *SEA)
: SEA(SEA), RunsOnHighLevelSil(RunsOnHighLevelSil) {}
bool processFunction(SILFunction &F, DominanceInfo *DT);
bool canHandle(SILInstruction *Inst);
private:
/// True if CSE is done on high-level SIL, i.e. semantic calls are not inlined
/// yet. In this case some semantic calls can be CSEd.
bool RunsOnHighLevelSil;
// NodeScope - almost a POD, but needs to call the constructors for the
// scoped hash tables so that a new scope gets pushed on. These are RAII so
// that the scope gets popped when the NodeScope is destroyed.
class NodeScope {
public:
NodeScope(ScopedHTType *availableValues) : Scope(*availableValues) {}
private:
NodeScope(const NodeScope &) = delete;
void operator=(const NodeScope &) = delete;
ScopedHTType::ScopeTy Scope;
};
// StackNode - contains all the needed information to create a stack for doing
// a depth first traversal of the tree. This includes scopes for values and
// loads as well as the generation. There is a child iterator so that the
// children do not need to be store separately.
class StackNode {
public:
StackNode(ScopedHTType *availableValues, DominanceInfoNode *n,
DominanceInfoNode::iterator child,
DominanceInfoNode::iterator end)
: Node(n), ChildIter(child), EndIter(end), Scopes(availableValues),
Processed(false) {}
// Accessors.
DominanceInfoNode *node() { return Node; }
DominanceInfoNode::iterator childIter() { return ChildIter; }
DominanceInfoNode *nextChild() {
DominanceInfoNode *child = *ChildIter;
++ChildIter;
return child;
}
DominanceInfoNode::iterator end() { return EndIter; }
bool isProcessed() { return Processed; }
void process() { Processed = true; }
private:
StackNode(const StackNode &) = delete;
void operator=(const StackNode &) = delete;
// Members.
DominanceInfoNode *Node;
DominanceInfoNode::iterator ChildIter;
DominanceInfoNode::iterator EndIter;
NodeScope Scopes;
bool Processed;
};
bool processNode(DominanceInfoNode *Node);
bool processOpenExistentialRef(OpenExistentialRefInst *Inst, ValueBase *V);
};
} // namespace swift
//===----------------------------------------------------------------------===//
// CSE Implementation
//===----------------------------------------------------------------------===//
bool CSE::processFunction(SILFunction &Fm, DominanceInfo *DT) {
std::vector<StackNode *> nodesToProcess;
// Tables that the pass uses when walking the domtree.
ScopedHTType AVTable;
AvailableValues = &AVTable;
bool Changed = false;
// Process the root node.
nodesToProcess.push_back(new StackNode(AvailableValues, DT->getRootNode(),
DT->getRootNode()->begin(),
DT->getRootNode()->end()));
// Process the stack.
while (!nodesToProcess.empty()) {
// Grab the first item off the stack. Set the current generation, remove
// the node from the stack, and process it.
StackNode *NodeToProcess = nodesToProcess.back();
// Check if the node needs to be processed.
if (!NodeToProcess->isProcessed()) {
// Process the node.
Changed |= processNode(NodeToProcess->node());
NodeToProcess->process();
} else if (NodeToProcess->childIter() != NodeToProcess->end()) {
// Push the next child onto the stack.
DominanceInfoNode *child = NodeToProcess->nextChild();
nodesToProcess.push_back(
new StackNode(AvailableValues, child, child->begin(), child->end()));
} else {
// It has been processed, and there are no more children to process,
// so delete it and pop it off the stack.
delete NodeToProcess;
nodesToProcess.pop_back();
}
} // while (!nodes...)
return Changed;
}
namespace {
// A very simple cloner for cloning instructions inside
// the same function. The only interesting thing it does
// is remapping the archetypes when it is required.
class InstructionCloner : public SILCloner<InstructionCloner> {
friend class SILCloner<InstructionCloner>;
friend class SILInstructionVisitor<InstructionCloner>;
SILInstruction *Result = nullptr;
public:
InstructionCloner(SILFunction *F) : SILCloner(*F) {}
static SILInstruction *doIt(SILInstruction *I) {
InstructionCloner TC(I->getFunction());
return TC.clone(I);
}
SILInstruction *clone(SILInstruction *I) {
visit(I);
return Result;
}
void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
assert(Orig->getFunction() == &getBuilder().getFunction() &&
"cloning between functions is not supported");
Result = Cloned;
SILCloner<InstructionCloner>::postProcess(Orig, Cloned);
}
SILValue getMappedValue(SILValue Value) {
return Value;
}
SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
};
} // end anonymous namespace
/// Update SIL basic block's arguments types which refer to opened
/// archetypes. Replace such types by performing type substitutions
/// according to the provided type substitution map.
static void updateBasicBlockArgTypes(SILBasicBlock *BB,
ArchetypeType *OldOpenedArchetype,
ArchetypeType *NewOpenedArchetype) {
// Check types of all BB arguments.
for (auto *Arg : BB->getPhiArguments()) {
if (!Arg->getType().hasOpenedExistential())
continue;
// Type of this BB argument uses an opened existential.
// Try to apply substitutions to it and if it produces a different type,
// use this type as new type of the BB argument.
auto OldArgType = Arg->getType();
auto NewArgType = OldArgType.subst(BB->getModule(),
[&](SubstitutableType *type) -> Type {
if (type == OldOpenedArchetype)
return NewOpenedArchetype;
return type;
},
MakeAbstractConformanceForGenericType());
if (NewArgType == Arg->getType())
continue;
// Replace the type of this BB argument. The type of a BBArg
// can only be changed using replaceBBArg, if the BBArg has no uses.
// So, make it look as if it has no uses.
// First collect all uses, before changing the type.
SmallVector<Operand *, 4> OriginalArgUses;
for (auto *ArgUse : Arg->getUses()) {
OriginalArgUses.push_back(ArgUse);
}
// Then replace all uses by an undef.
Arg->replaceAllUsesWith(SILUndef::get(Arg->getType(), *BB->getParent()));
// Replace the type of the BB argument.
auto *NewArg = BB->replacePhiArgument(Arg->getIndex(), NewArgType,
Arg->getOwnershipKind(),
Arg->getDecl());
// Restore all uses to refer to the BB argument with updated type.
for (auto ArgUse : OriginalArgUses) {
ArgUse->set(NewArg);
}
}
}
/// Handle CSE of open_existential_ref instructions.
/// Returns true if uses of open_existential_ref can
/// be replaced by a dominating instruction.
/// \Inst is the open_existential_ref instruction
/// \V is the dominating open_existential_ref instruction
bool CSE::processOpenExistentialRef(OpenExistentialRefInst *Inst,
ValueBase *V) {
// All the open instructions are single-value instructions.
auto VI = dyn_cast<SingleValueInstruction>(V);
if (!VI) return false;
llvm::SmallSetVector<SILInstruction *, 16> Candidates;
auto OldOpenedArchetype = getOpenedArchetypeOf(Inst);
auto NewOpenedArchetype = getOpenedArchetypeOf(VI);
// Collect all candidates that may contain opened archetypes
// that need to be replaced.
for (auto Use : Inst->getUses()) {
auto User = Use->getUser();
if (!User->getTypeDependentOperands().empty()) {
if (canHandle(User)) {
auto It = AvailableValues->begin(User);
if (It != AvailableValues->end()) {
return false;
}
}
Candidates.insert(User);
}
if (!isa<TermInst>(User))
continue;
// The current use of the opened archetype is a terminator instruction.
// Check if any of the successor BBs uses this opened archetype in the
// types of its basic block arguments. If this is the case, replace
// those uses by the new opened archetype.
auto Successors = User->getParent()->getSuccessorBlocks();
for (auto Successor : Successors) {
if (Successor->args_empty())
continue;
// If a BB has any arguments, update their types if necessary.
updateBasicBlockArgTypes(Successor,
OldOpenedArchetype,
NewOpenedArchetype);
}
}
// Now process candidates.
// TODO: Move it to CSE instance to avoid recreating it every time?
SILOpenedArchetypesTracker OpenedArchetypesTracker(Inst->getFunction());
// Register the new archetype to be used.
OpenedArchetypesTracker.registerOpenedArchetypes(VI);
// Use a cloner. It makes copying the instruction and remapping of
// opened archetypes trivial.
InstructionCloner Cloner(Inst->getFunction());
Cloner.registerOpenedExistentialRemapping(
OldOpenedArchetype->castTo<ArchetypeType>(), NewOpenedArchetype);
auto &Builder = Cloner.getBuilder();
Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
llvm::SmallPtrSet<SILInstruction *, 16> Processed;
// Now clone each candidate and replace the opened archetype
// by a dominating one.
while (!Candidates.empty()) {
auto Candidate = Candidates.pop_back_val();
if (Processed.count(Candidate))
continue;
// Compute if a candidate depends on the old opened archetype.
// It always does if it has any type-dependent operands.
bool DependsOnOldOpenedArchetype =
!Candidate->getTypeDependentOperands().empty();
// Look for dependencies propagated via the candidate's results.
for (auto CandidateResult : Candidate->getResults()) {
if (CandidateResult->use_empty() ||
!CandidateResult->getType().hasOpenedExistential())
continue;
// Check if the result type depends on this specific opened existential.
auto ResultDependsOnOldOpenedArchetype =
CandidateResult->getType().getASTType().findIf(
[&OldOpenedArchetype](Type t) -> bool {
return (CanType(t) == OldOpenedArchetype);
});
// If it does, the candidate depends on the opened existential.
if (ResultDependsOnOldOpenedArchetype) {
DependsOnOldOpenedArchetype |= ResultDependsOnOldOpenedArchetype;
// The users of this candidate are new candidates.
for (auto Use : CandidateResult->getUses()) {
Candidates.insert(Use->getUser());
}
}
}
// Remember that this candidate was processed already.
Processed.insert(Candidate);
// No need to clone if there is no dependency on the old opened archetype.
if (!DependsOnOldOpenedArchetype)
continue;
Builder.getOpenedArchetypes().addOpenedArchetypeOperands(
Candidate->getTypeDependentOperands());
Builder.setInsertionPoint(Candidate);
auto NewI = Cloner.clone(Candidate);
// Result types of candidate's uses instructions may be using this archetype.
// Thus, we need to try to replace it there.
Candidate->replaceAllUsesPairwiseWith(NewI);
eraseFromParentWithDebugInsts(Candidate);
}
return true;
}
bool CSE::processNode(DominanceInfoNode *Node) {
SILBasicBlock *BB = Node->getBlock();
bool Changed = false;
// See if any instructions in the block can be eliminated. If so, do it. If
// not, add them to AvailableValues. Assume the block terminator can't be
// erased.
for (SILBasicBlock::iterator nextI = BB->begin(), E = BB->end();
nextI != E;) {
SILInstruction *Inst = &*nextI;
++nextI;
LLVM_DEBUG(llvm::dbgs() << "SILCSE VISITING: " << *Inst << "\n");
// Dead instructions should just be removed.
if (isInstructionTriviallyDead(Inst)) {
LLVM_DEBUG(llvm::dbgs() << "SILCSE DCE: " << *Inst << '\n');
nextI = eraseFromParentWithDebugInsts(Inst);
Changed = true;
++NumSimplify;
continue;
}
// If the instruction can be simplified (e.g. X+0 = X) then replace it with
// its simpler value.
if (SILValue V = simplifyInstruction(Inst)) {
LLVM_DEBUG(llvm::dbgs() << "SILCSE SIMPLIFY: " << *Inst << " to: " << *V
<< '\n');
nextI = replaceAllSimplifiedUsesAndErase(Inst, V);
Changed = true;
++NumSimplify;
continue;
}
// If this is not a simple instruction that we can value number, skip it.
if (!canHandle(Inst))
continue;
// If an instruction can be handled here, then it must also be handled
// in isIdenticalTo, otherwise looking up a key in the map with fail to
// match itself.
assert(Inst->isIdenticalTo(Inst) &&
"Inst must match itself for map to work");
// Now that we know we have an instruction we understand see if the
// instruction has an available value. If so, use it.
if (SILInstruction *AvailInst = AvailableValues->lookup(Inst)) {
LLVM_DEBUG(llvm::dbgs() << "SILCSE CSE: " << *Inst << " to: "
<< *AvailInst << '\n');
// Instructions producing a new opened archetype need a special handling,
// because replacing these instructions may require a replacement
// of the opened archetype type operands in some of the uses.
if (!isa<OpenExistentialRefInst>(Inst)
|| processOpenExistentialRef(
cast<OpenExistentialRefInst>(Inst),
cast<OpenExistentialRefInst>(AvailInst))) {
// processOpenExistentialRef may delete instructions other than Inst, so
// nextI must be reassigned.
nextI = std::next(Inst->getIterator());
Inst->replaceAllUsesPairwiseWith(AvailInst);
Inst->eraseFromParent();
Changed = true;
++NumCSE;
continue;
}
}
// Otherwise, just remember that this value is available.
AvailableValues->insert(Inst, Inst);
LLVM_DEBUG(llvm::dbgs() << "SILCSE Adding to value table: " << *Inst
<< " -> " << *Inst << "\n");
}
return Changed;
}
bool CSE::canHandle(SILInstruction *Inst) {
if (auto *AI = dyn_cast<ApplyInst>(Inst)) {
if (!AI->mayReadOrWriteMemory())
return true;
if (RunsOnHighLevelSil) {
ArraySemanticsCall SemCall(AI);
switch (SemCall.getKind()) {
case ArrayCallKind::kGetCount:
case ArrayCallKind::kGetCapacity:
case ArrayCallKind::kCheckIndex:
case ArrayCallKind::kCheckSubscript:
return SemCall.hasGuaranteedSelf();
default:
return false;
}
}
// We can CSE function calls which do not read or write memory and don't
// have any other side effects.
FunctionSideEffects Effects;
SEA->getCalleeEffects(Effects, AI);
// Note that the function also may not contain any retains. And there are
// functions which are read-none and have a retain, e.g. functions which
// _convert_ a global_addr to a reference and retain it.
auto MB = Effects.getMemBehavior(RetainObserveKind::ObserveRetains);
if (MB == SILInstruction::MemoryBehavior::None)
return true;
return false;
}
if (auto *BI = dyn_cast<BuiltinInst>(Inst)) {
// Although the onFastPath builtin has no side-effects we don't want to
// (re-)move it.
if (BI->getBuiltinInfo().ID == BuiltinValueKind::OnFastPath)
return false;
return !BI->mayReadOrWriteMemory();
}
if (auto *EMI = dyn_cast<ExistentialMetatypeInst>(Inst)) {
return !EMI->getOperand()->getType().isAddress();
}
switch (Inst->getKind()) {
case SILInstructionKind::ClassMethodInst:
case SILInstructionKind::SuperMethodInst:
case SILInstructionKind::FunctionRefInst:
case SILInstructionKind::GlobalAddrInst:
case SILInstructionKind::IntegerLiteralInst:
case SILInstructionKind::FloatLiteralInst:
case SILInstructionKind::StringLiteralInst:
case SILInstructionKind::StructInst:
case SILInstructionKind::StructExtractInst:
case SILInstructionKind::StructElementAddrInst:
case SILInstructionKind::TupleInst:
case SILInstructionKind::TupleExtractInst:
case SILInstructionKind::TupleElementAddrInst:
case SILInstructionKind::MetatypeInst:
case SILInstructionKind::ValueMetatypeInst:
case SILInstructionKind::ObjCProtocolInst:
case SILInstructionKind::RefElementAddrInst:
case SILInstructionKind::RefTailAddrInst:
case SILInstructionKind::ProjectBoxInst:
case SILInstructionKind::IndexRawPointerInst:
case SILInstructionKind::IndexAddrInst:
case SILInstructionKind::PointerToAddressInst:
case SILInstructionKind::AddressToPointerInst:
case SILInstructionKind::CondFailInst:
case SILInstructionKind::EnumInst:
case SILInstructionKind::UncheckedEnumDataInst:
case SILInstructionKind::UncheckedTrivialBitCastInst:
case SILInstructionKind::UncheckedBitwiseCastInst:
case SILInstructionKind::RefToRawPointerInst:
case SILInstructionKind::RawPointerToRefInst:
case SILInstructionKind::UpcastInst:
case SILInstructionKind::ThickToObjCMetatypeInst:
case SILInstructionKind::ObjCToThickMetatypeInst:
case SILInstructionKind::UncheckedRefCastInst:
case SILInstructionKind::UncheckedAddrCastInst:
case SILInstructionKind::ObjCMetatypeToObjectInst:
case SILInstructionKind::ObjCExistentialMetatypeToObjectInst:
case SILInstructionKind::SelectEnumInst:
case SILInstructionKind::SelectValueInst:
case SILInstructionKind::RefToBridgeObjectInst:
case SILInstructionKind::BridgeObjectToRefInst:
case SILInstructionKind::BridgeObjectToWordInst:
case SILInstructionKind::ClassifyBridgeObjectInst:
case SILInstructionKind::ValueToBridgeObjectInst:
case SILInstructionKind::ThinFunctionToPointerInst:
case SILInstructionKind::PointerToThinFunctionInst:
case SILInstructionKind::MarkDependenceInst:
case SILInstructionKind::OpenExistentialRefInst:
case SILInstructionKind::WitnessMethodInst:
// Intentionally we don't handle (prev_)dynamic_function_ref.
// They change at runtime.
#define LOADABLE_REF_STORAGE(Name, ...) \
case SILInstructionKind::RefTo##Name##Inst: \
case SILInstructionKind::Name##ToRefInst:
#include "swift/AST/ReferenceStorage.def"
return true;
default:
return false;
}
}
using ApplyWitnessPair = std::pair<ApplyInst *, WitnessMethodInst *>;
/// Returns the Apply and WitnessMethod instructions that use the
/// open_existential_addr instructions, or null if at least one of the
/// instructions is missing.
static ApplyWitnessPair getOpenExistentialUsers(OpenExistentialAddrInst *OE) {
ApplyInst *AI = nullptr;
WitnessMethodInst *WMI = nullptr;
ApplyWitnessPair Empty = std::make_pair(nullptr, nullptr);
for (auto *UI : getNonDebugUses(OE)) {
auto *User = UI->getUser();
if (!isa<WitnessMethodInst>(User) &&
User->isTypeDependentOperand(UI->getOperandNumber()))
continue;
// Check that we have a single Apply user.
if (auto *AA = dyn_cast<ApplyInst>(User)) {
if (AI)
return Empty;
AI = AA;
continue;
}
// Check that we have a single WMI user.
if (auto *W = dyn_cast<WitnessMethodInst>(User)) {
if (WMI)
return Empty;
WMI = W;
continue;
}
// Unknown instruction.
return Empty;
}
// Both instructions need to exist.
if (!WMI || !AI)
return Empty;
// Make sure that the WMI and AI match.
if (AI->getCallee() != WMI)
return Empty;
// We have exactly the pattern that we expected.
return std::make_pair(AI, WMI);
}
/// Try to CSE the users of \p From to the users of \p To.
/// The original users of \p To are passed in ToApplyWitnessUsers.
/// Returns true on success.
static bool tryToCSEOpenExtCall(OpenExistentialAddrInst *From,
OpenExistentialAddrInst *To,
ApplyWitnessPair ToApplyWitnessUsers,
DominanceInfo *DA) {
assert(From != To && "Can't replace instruction with itself");
ApplyInst *FromAI = nullptr;
ApplyInst *ToAI = nullptr;
WitnessMethodInst *FromWMI = nullptr;
WitnessMethodInst *ToWMI = nullptr;
std::tie(FromAI, FromWMI) = getOpenExistentialUsers(From);
std::tie(ToAI, ToWMI) = ToApplyWitnessUsers;
// Make sure that the OEA instruction has exactly two expected users.
if (!FromAI || !ToAI || !FromWMI || !ToWMI)
return false;
// Make sure we are calling the same method.
if (FromWMI->getMember() != ToWMI->getMember())
return false;
// We are going to reuse the TO-WMI, so make sure it dominates the call site.
if (!DA->properlyDominates(ToWMI, FromWMI))
return false;
SILBuilder Builder(FromAI);
// Make archetypes used by the ToAI available to the builder.
SILOpenedArchetypesTracker OpenedArchetypesTracker(FromAI->getFunction());
OpenedArchetypesTracker.registerUsedOpenedArchetypes(ToAI);
Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
assert(FromAI->getArguments().size() == ToAI->getArguments().size() &&
"Invalid number of arguments");
// Don't handle any apply instructions that involve substitutions.
if (ToAI->getSubstitutionMap().getReplacementTypes().size() != 1)
return false;
// Prepare the Apply args.
SmallVector<SILValue, 8> Args;
for (auto Op : FromAI->getArguments()) {
Args.push_back(Op == From ? To : Op);
}
ApplyInst *NAI = Builder.createApply(ToAI->getLoc(), ToWMI,
ToAI->getSubstitutionMap(), Args,
ToAI->isNonThrowing());
FromAI->replaceAllUsesWith(NAI);
FromAI->eraseFromParent();
NumOpenExtRemoved++;
return true;
}
/// Try to CSE the users of the protocol that's passed in argument \p Arg.
/// \returns True if some instructions were modified.
static bool CSExistentialInstructions(SILFunctionArgument *Arg,
DominanceInfo *DA) {
ParameterConvention Conv = Arg->getKnownParameterInfo().getConvention();
// We can assume that the address of Proto does not alias because the
// calling convention is In or In-guaranteed.
bool MayAlias = Conv != ParameterConvention::Indirect_In_Guaranteed &&
Conv != ParameterConvention::Indirect_In;
if (MayAlias)
return false;
// Now check that the only uses of the protocol are witness_method,
// open_existential_addr and destroy_addr. Also, collect all of the 'opens'.
llvm::SmallVector<OpenExistentialAddrInst*, 8> Opens;
for (auto *UI : getNonDebugUses(Arg)) {
auto *User = UI->getUser();
if (auto *Open = dyn_cast<OpenExistentialAddrInst>(User)) {
Opens.push_back(Open);
continue;
}
if (isa<WitnessMethodInst>(User) || isa<DestroyAddrInst>(User))
continue;
// Bail out if we found an instruction that we can't handle.
return false;
}
// Find the best dominating 'open' for each open existential.
llvm::SmallVector<OpenExistentialAddrInst*, 8> TopDominator(Opens);
bool Changed = false;
// Try to CSE the users of the current open_existential_addr instruction with
// one of the other open_existential_addr that dominate it.
int NumOpenInstr = Opens.size();
for (int i = 0; i < NumOpenInstr; i++) {
// Try to find a better dominating 'open' for the i-th instruction.
OpenExistentialAddrInst *SomeOpen = TopDominator[i];
for (int j = 0; j < NumOpenInstr; j++) {
if (i == j || TopDominator[i] == TopDominator[j])
continue;
OpenExistentialAddrInst *DominatingOpen = TopDominator[j];
if (DominatingOpen->getOperand() != SomeOpen->getOperand())
continue;
if (DA->properlyDominates(DominatingOpen, SomeOpen)) {
// We found an open instruction that DominatingOpen dominates:
TopDominator[i] = TopDominator[j];
}
}
}
// Inspect all of the open_existential_addr instructions and record the
// apply-witness users. We need to save the original Apply-Witness users
// because we'll be adding new users and we need to make sure that we can
// find the original users.
llvm::SmallVector<ApplyWitnessPair, 8> OriginalAW;
for (int i=0; i < NumOpenInstr; i++) {
OriginalAW.push_back(getOpenExistentialUsers(TopDominator[i]));
}
// Perform the CSE for the open_existential_addr instruction and their
// dominating instruction.
for (int i=0; i < NumOpenInstr; i++) {
if (Opens[i] != TopDominator[i])
Changed |= tryToCSEOpenExtCall(Opens[i], TopDominator[i],
OriginalAW[i], DA);
}
return Changed;
}
/// Detect multiple calls to existential members and try to CSE the instructions
/// that perform the method lookup (the open_existential_addr and
/// witness_method):
///
/// open_existential_addr %0 : $*Pingable to $*@opened("1E467EB8-...")
/// witness_method $@opened("1E467EB8-...") Pingable, #Pingable.ping!1, %2
/// apply %3<@opened("1E467EB8-...") Pingable>(%2)
///
/// \returns True if some instructions were modified.
static bool CSEExistentialCalls(SILFunction *Func, DominanceInfo *DA) {
bool Changed = false;
for (auto *Arg : Func->getArgumentsWithoutIndirectResults()) {
if (Arg->getType().isExistentialType()) {
auto *FArg = cast<SILFunctionArgument>(Arg);
Changed |= CSExistentialInstructions(FArg, DA);
}
}
return Changed;
}
namespace {
class SILCSE : public SILFunctionTransform {
/// True if CSE is done on high-level SIL, i.e. semantic calls are not inlined
/// yet. In this case some semantic calls can be CSEd.
/// We only CSE semantic calls on high-level SIL because we can be sure that
/// e.g. an Array as SILValue is really immutable (including its content).
bool RunsOnHighLevelSil;
void run() override {
// FIXME: We should be able to support ownership.
if (getFunction()->hasOwnership())
return;
LLVM_DEBUG(llvm::dbgs() << "***** CSE on function: "
<< getFunction()->getName() << " *****\n");
DominanceAnalysis* DA = getAnalysis<DominanceAnalysis>();
auto *SEA = PM->getAnalysis<SideEffectAnalysis>();
CSE C(RunsOnHighLevelSil, SEA);
bool Changed = false;
// Perform the traditional CSE.
Changed |= C.processFunction(*getFunction(), DA->get(getFunction()));
// Perform CSE of existential and witness_method instructions.
Changed |= CSEExistentialCalls(getFunction(),
DA->get(getFunction()));
if (Changed) {
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
}
}
public:
SILCSE(bool RunsOnHighLevelSil) : RunsOnHighLevelSil(RunsOnHighLevelSil) {}
};
} // end anonymous namespace
SILTransform *swift::createCSE() {
return new SILCSE(false);
}
SILTransform *swift::createHighLevelCSE() {
return new SILCSE(true);
}