Files
swift-mirror/lib/SILPasses/LoadStoreOpts.cpp
2015-04-29 17:05:09 +00:00

1456 lines
53 KiB
C++

//===---- LoadStoreOpts.cpp - SIL Load/Store Optimizations Forwarding -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This pass eliminates redundent loads, dead stores, and performs load
// forwarding.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "load-store-opts"
#include "swift/SILPasses/Passes.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SILAnalysis/AliasAnalysis.h"
#include "swift/SILAnalysis/PostOrderAnalysis.h"
#include "swift/SILAnalysis/DominanceAnalysis.h"
#include "swift/SILAnalysis/ValueTracking.h"
#include "swift/SILPasses/Utils/Local.h"
#include "swift/SILPasses/Utils/CFG.h"
#include "swift/SILPasses/Transforms.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
using namespace swift;
STATISTIC(NumDeadStores, "Number of dead stores removed");
STATISTIC(NumDupLoads, "Number of dup loads removed");
STATISTIC(NumForwardedLoads, "Number of loads forwarded");
//===----------------------------------------------------------------------===//
// Utility Functions
//===----------------------------------------------------------------------===//
/// Returns true if this is an instruction that may have side effects in a
/// general sense but are inert from a load store perspective.
static bool isLSForwardingInertInstruction(SILInstruction *Inst) {
switch (Inst->getKind()) {
case ValueKind::StrongRetainInst:
case ValueKind::RetainValueInst:
case ValueKind::DeallocStackInst:
case ValueKind::CondFailInst:
return true;
default:
return false;
}
}
static SILValue getForwardingValueForLS(const SILInstruction *I) {
if (auto *SI = dyn_cast<StoreInst>(I))
return SI->getSrc();
return cast<LoadInst>(I);
}
static SILValue getAddressForLS(const SILInstruction *I) {
if (auto *SI = dyn_cast<StoreInst>(I))
return SI->getDest();
return cast<LoadInst>(I)->getOperand();
}
static SILType getForwardingTypeForLS(const SILInstruction *I) {
return getForwardingValueForLS(I).getType();
}
//===----------------------------------------------------------------------===//
// Forwarding Feasability Analysis
//===----------------------------------------------------------------------===//
namespace {
enum class ForwardingKind {
Normal,
UncheckedAddress
};
/// This is a move-only structure. Thus is has a private default constructor and
/// a deleted copy constructor.
class ForwardingAnalysis {
ForwardingKind Kind;
UncheckedAddrCastInst *UADCI;
Optional<ProjectionPath> Path;
ForwardingAnalysis(ForwardingKind Kind,
UncheckedAddrCastInst *UADCI,
Optional<ProjectionPath> &&P)
: Kind(Kind), UADCI(UADCI), Path(std::move(*P)) {}
ForwardingAnalysis(ForwardingKind Kind,
Optional<ProjectionPath> &&P)
: Kind(Kind), UADCI(nullptr), Path(std::move(*P)) {}
public:
ForwardingAnalysis() = delete;
ForwardingAnalysis(const ForwardingAnalysis &) = delete;
ForwardingAnalysis(ForwardingAnalysis &&FFA) = default;
static Optional<ForwardingAnalysis>
canForwardAddrToUncheckedAddrToLd(SILValue Address, LoadInst *LI,
UncheckedAddrCastInst *UADCI);
static Optional<ForwardingAnalysis>
canForwardAddrToLd(SILValue Address, LoadInst *LI);
SILValue forwardAddr(SILValue Addr, SILValue StoredValue,
LoadInst *LI);
private:
SILValue forwardAddrToLdWithExtractPath(SILValue Address,
SILValue StoredValue,
SILInstruction *Inst,
SILValue InstOp);
SILValue forwardAddrToUncheckedCastToLd(SILValue Address,
SILValue StoredValue,
LoadInst *LI);
};
} // end anonymous namespace
/// Given an unchecked_addr_cast with various address projections using it,
/// check if we can forward the stored value.
Optional<ForwardingAnalysis>
ForwardingAnalysis::
canForwardAddrToUncheckedAddrToLd(SILValue Address,
LoadInst *LI,
UncheckedAddrCastInst *UADCI) {
assert(LI->getOperand().stripAddressProjections() == UADCI &&
"We assume that the UADCI is the load's address stripped of "
"address projections.");
// First grab the address operand of our UADCI.
SILValue UADCIOp = UADCI->getOperand();
// Make sure that this is equal to our address. If not, bail.
if (UADCIOp != Address)
return llvm::NoneType::None;
// Construct the relevant bitcast.
SILModule &Mod = UADCI->getModule();
SILType InputTy = UADCI->getOperand().getType();
SILType OutputTy = UADCI->getType();
bool InputIsTrivial = InputTy.isTrivial(Mod);
bool OutputIsTrivial = OutputTy.isTrivial(Mod);
// If either are generic, bail.
if (InputTy.hasArchetype() || OutputTy.hasArchetype())
return llvm::NoneType::None;
// If we have a trivial input and a non-trivial output bail.
if (InputIsTrivial && !OutputIsTrivial) {
return llvm::NoneType::None;
}
// The structs could have different size. We have code in the stdlib that
// casts pointers to differently sized integer types. This code prevents
// that we bitcast the values.
if (OutputTy.getStructOrBoundGenericStruct() &&
InputTy.getStructOrBoundGenericStruct())
return llvm::NoneType::None;
SILValue LdAddr = LI->getOperand();
auto P = ProjectionPath::getAddrProjectionPath(UADCI, LdAddr);
if (!P)
return llvm::NoneType::None;
return ForwardingAnalysis(ForwardingKind::UncheckedAddress,
UADCI, std::move(P));
}
Optional<ForwardingAnalysis>
ForwardingAnalysis::
canForwardAddrToLd(SILValue Address, LoadInst *LI) {
// First if we have a store + unchecked_addr_cast + load, try to forward the
// value the store using a bitcast.
SILValue LIOpWithoutProjs = LI->getOperand().stripAddressProjections();
if (auto *UADCI = dyn_cast<UncheckedAddrCastInst>(LIOpWithoutProjs))
return ForwardingAnalysis::canForwardAddrToUncheckedAddrToLd(Address, LI,
UADCI);
// Attempt to find the projection path from Address -> Load->getOperand().
// If we failed to find the path, return an empty value early.
auto P = ProjectionPath::getAddrProjectionPath(Address, LI->getOperand());
if (!P)
return llvm::NoneType::None;
return ForwardingAnalysis(ForwardingKind::Normal, std::move(P));
}
/// Given an unchecked_addr_cast with various address projections using it,
/// rewrite the forwarding stored value to a bitcast + the relevant extract
/// operations.
SILValue
ForwardingAnalysis::
forwardAddrToUncheckedCastToLd(SILValue Address, SILValue StoredValue,
LoadInst *LI) {
assert(UADCI && "UADCI is assumed to be non-null here");
// Construct the relevant bitcast.
SILModule &Mod = UADCI->getModule();
SILType OutputTy = UADCI->getType();
bool OutputIsTrivial = OutputTy.isTrivial(Mod);
SILBuilderWithScope<1> B(LI);
SILValue CastValue;
// If the output is trivial, we have a trivial bit cast.
if (OutputIsTrivial) {
CastValue = B.createUncheckedTrivialBitCast(UADCI->getLoc(), StoredValue,
OutputTy.getObjectType());
} else {
// Otherwise, both must have some sort of reference counts on them. Insert
// the ref count cast.
CastValue = B.createUncheckedRefBitCast(UADCI->getLoc(), StoredValue,
OutputTy.getObjectType());
}
// Then try to construct an extract path from the UADCI to the Address.
SILValue ExtractPath =
forwardAddrToLdWithExtractPath(UADCI, CastValue,
LI, LI->getOperand());
assert(ExtractPath && "Already checked the feasibility.");
assert(ExtractPath.getType() == LI->getType().getObjectType() &&
"Must have same types here.");
return ExtractPath;
}
SILValue
ForwardingAnalysis::forwardAddr(SILValue Addr, SILValue StoredValue,
LoadInst *LI) {
// First if we have a store + unchecked_addr_cast + load, try to forward the
// value the store using a bitcast.
if (Kind == ForwardingKind::UncheckedAddress)
return forwardAddrToUncheckedCastToLd(Addr, StoredValue, LI);
assert(Kind == ForwardingKind::Normal && "The default kind is Normal.");
// Next, try to promote partial loads from stores. If this fails, it will
// return SILValue(), which is also our failure condition.
return forwardAddrToLdWithExtractPath(Addr, StoredValue, LI,
LI->getOperand());
}
/// Given the already emitted load PrevLI, see if we can find a projection
/// address path to LI. If we can, emit the corresponding aggregate projection
/// insts and return the last such inst.
SILValue
ForwardingAnalysis::
forwardAddrToLdWithExtractPath(SILValue Address, SILValue StoredValue,
SILInstruction *Inst, SILValue InstOp) {
// If we found a projection path, but there are no projections, then the two
// loads must be the same, return PrevLI.
if (!Path || Path->empty())
return StoredValue;
// Ok, at this point we know that we can construct our aggregate projections
// from our list of address projections.
SILValue LastExtract = StoredValue;
SILBuilderWithScope<16> Builder(Inst);
// Construct the path!
for (auto PI = Path->rbegin(), PE = Path->rend(); PI != PE; ++PI) {
LastExtract = PI->createValueProjection(Builder, Inst->getLoc(),
LastExtract).get();
}
// Return the last extract we created.
return LastExtract;
}
/// Given an address \p Address and a value \p Value stored there that is then
/// loaded or partially loaded by \p LI, forward the value with the appropriate
/// extracts. This is the main entry point to the forwarding feasability code.
static SILValue tryToForwardAddressValueToLoad(SILValue Address,
SILValue StoredValue,
LoadInst *LI) {
auto CheckResult = ForwardingAnalysis::canForwardAddrToLd(Address, LI);
if (!CheckResult)
return SILValue();
return CheckResult->forwardAddr(Address, StoredValue, LI);
}
//===----------------------------------------------------------------------===//
// LSContext Interface
//===----------------------------------------------------------------------===//
namespace {
class LSBBForwarder;
/// This class stores global state that we use when processing and also drives
/// the computation. We put its interface at the top for use in other parts of
/// the pass which may want to use this global information.
class LSContext {
/// The alias analysis that we will use during all computations.
AliasAnalysis *AA;
/// The post dominance analysis that we use for dead store elimination.
PostDominanceInfo *PDI;
/// The range that we use to iterate over the reverse post order of the given
/// function.
PostOrderAnalysis::reverse_range ReversePostOrder;
/// A map from each BasicBlock to its index in the BBIDToForwarderMap.
///
/// TODO: Each block does not need its own LSBBForwarder instance. Only
/// the set of reaching loads and stores is specific to the block.
llvm::DenseMap<SILBasicBlock *, unsigned> BBToBBIDMap;
/// A "map" from a BBID (which is just an index) to an LSBBForwarder.
std::vector<LSBBForwarder> BBIDToForwarderMap;
public:
LSContext(AliasAnalysis *AA, PostDominanceInfo *PDI,
PostOrderAnalysis::reverse_range RPOT);
LSContext(const LSContext &) = delete;
LSContext(LSContext &&) = default;
~LSContext() = default;
bool runIteration();
AliasAnalysis *getAA() const { return AA; }
PostDominanceInfo *getPDI() const { return PDI; }
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// LSValue
//===----------------------------------------------------------------------===//
namespace {
/// This class represents either a single value or a covering of values that we
/// can load forward from via the introdution of a SILArgument. This enables us
/// to treat the case of having one value or multiple values and load and store
/// cases all at once abstractly and cleanly.
class LSValue {
/// The "parent" basic block which this LSValue originated in.
///
/// In the case where we are tracking one value this is the BB in which the
/// actual value originated. In the case in which we are tracking a covering
/// set of loads, this is the BB where if we forward this load value, we will
/// need to insert a SILArgument.
SILBasicBlock *ParentBB;
/// The individual inst or covering inst set that this LSValue represents.
llvm::TinyPtrVector<SILInstruction *> Insts;
/// The lazily computed value that can be used to forward this LSValue.
///
/// In the case where we have a single value this is always initialized. In
/// the case where we are handling a covering set, this is initially null and
/// when we insert the PHI node, this is set to the SILArgument which
/// represents the PHI node.
///
/// In the case where we are dealing with loads this is the loaded value or a
/// phi derived from a covering set of loaded values. In the case where we are
/// dealing with stores, this is the value that is stored or a phi of such
/// values.
SILValue ForwardingValue;
public:
LSValue(SILInstruction *NewInst)
: ParentBB(NewInst->getParent()), Insts(NewInst),
ForwardingValue(getForwardingValueForLS(NewInst)) {}
LSValue(SILBasicBlock *NewParentBB, ArrayRef<SILInstruction *> NewInsts);
LSValue(SILBasicBlock *NewParentBB, ArrayRef<LoadInst *> NewInsts);
LSValue(SILBasicBlock *NewParentBB, ArrayRef<StoreInst *> NewInsts);
bool operator==(const LSValue &Other) const;
/// Return the SILValue necessary for forwarding the given LSValue.
///
/// *NOTE* This will create a PHI node if we have not created one yet if we
/// have a covering set.
SILValue getForwardingValue();
/// Returns true if this LSValue aliases the given instruction.
bool aliasingWrite(AliasAnalysis *AA, SILInstruction *Inst) const {
// If we have a single inst, just get the forwarding value and compare if
// they alias.
if (isSingleInst())
return AA->mayWriteToMemory(Inst, getAddressForLS(getInst()));
// Otherwise, loop over all of our forwaring insts and return true if any of
// them alias Inst.
for (auto &I : getInsts())
if (AA->mayWriteToMemory(Inst, getAddressForLS(I)))
return true;
return false;
}
bool aliasingRead(AliasAnalysis *AA, SILInstruction *Inst) const {
// If we have a single inst, just get the forwarding value and compare if
// they alias.
if (isSingleInst())
return AA->mayReadFromMemory(Inst, getAddressForLS(getInst()));
// Otherwise, loop over all of our forwaring insts and return true if any of
// them alias Inst.
for (auto &I : getInsts())
if (AA->mayReadFromMemory(Inst, getAddressForLS(I)))
return true;
return false;
}
/// Returns the set of insts represented by this LSValue.
ArrayRef<SILInstruction *> getInsts() const { return Insts; }
MutableArrayRef<SILInstruction *> getInsts() { return Insts; }
#ifndef NDEBUG
friend raw_ostream &operator<<(raw_ostream &os, const LSValue &Val) {
os << "value in bb" << Val.ParentBB->getDebugID() << ": " <<
Val.ForwardingValue;
for (SILInstruction *I : Val.Insts) {
os << " " << *I;
}
return os;
}
#endif
protected:
/// Returns true if this LSValue represents a singular inst instruction.
bool isSingleInst() const { return Insts.size() == 1; }
/// Returns true if this LSValue represents a covering set of insts.
bool isCoveringInst() const { return Insts.size() > 1; }
/// Returns a singular inst if we are tracking a singular inst. Asserts
/// otherwise.
SILInstruction *getInst() const {
assert(isSingleInst() && "Can only getLoad() if this is a singular load");
return Insts[0];
}
};
} // end anonymous namespace
LSValue::LSValue(SILBasicBlock *NewParentBB,
ArrayRef<SILInstruction *> NewInsts)
: ParentBB(NewParentBB), Insts(), ForwardingValue() {
std::copy(NewInsts.begin(), NewInsts.end(), Insts.begin());
// Sort Insts so we can trivially compare two LSValues.
std::sort(Insts.begin(), Insts.end());
}
LSValue::LSValue(SILBasicBlock *NewParentBB,
ArrayRef<LoadInst *> NewInsts)
: ParentBB(NewParentBB), Insts(), ForwardingValue() {
std::copy(NewInsts.begin(), NewInsts.end(), Insts.begin());
// Sort Insts so we can trivially compare two LSValues.
std::sort(Insts.begin(), Insts.end());
}
LSValue::LSValue(SILBasicBlock *NewParentBB,
ArrayRef<StoreInst *> NewInsts)
: ParentBB(NewParentBB), Insts(), ForwardingValue() {
std::copy(NewInsts.begin(), NewInsts.end(), Insts.begin());
// Sort Insts so we can trivially compare two LSValues.
std::sort(Insts.begin(), Insts.end());
}
/// Return the SILValue necessary for forwarding the given LSValue. *NOTE*
/// This will create a PHI node if we have not created one yet if we have a
/// covering set.
SILValue LSValue::getForwardingValue() {
// If we already have a forwarding value, just return it.
if (ForwardingValue)
return ForwardingValue;
// Otherwise, we must have a covering set of loads. Create the PHI and set
// forwarding value to it.
assert(isCoveringInst() &&
"Must have a covering inst at this point since "
"if we have a singular inst ForwardingValue is set in the "
"constructor.");
// We only support adding arguments to cond_br and br. If any predecessor
// does not have such a terminator, return an empty SILValue().
//
// *NOTE* There is an assertion in addNewEdgeValueToBranch that will throw
// if we do not do this early.
// *NOTE* This is a strong argument in favor of representing PHI nodes
// separately from SILArguments.
if (std::any_of(ParentBB->pred_begin(), ParentBB->pred_end(),
[](SILBasicBlock *Pred) -> bool {
TermInst *TI = Pred->getTerminator();
return !isa<CondBranchInst>(TI) || !isa<BranchInst>(TI);
}))
return SILValue();
// Create the new SILArgument and set ForwardingValue to it.
ForwardingValue = ParentBB->createBBArg(getForwardingTypeForLS(Insts[0]));
// Update all edges. We do not create new edges in between BBs so this
// information should always be correct.
for (SILInstruction *I : getInsts())
addNewEdgeValueToBranch(I->getParent()->getTerminator(), ParentBB,
getForwardingValueForLS(I));
/// Return our new forwarding value.
return ForwardingValue;
}
/// We use the fact that LSValues always have items sorted by pointer address to
/// compare the two instruction lists.
bool LSValue::operator==(const LSValue &Other) const {
if (Insts.size() != Other.Insts.size())
return false;
for (unsigned i : indices(Insts))
if (Insts[i] != Other.Insts[i])
return false;
return true;
}
//===----------------------------------------------------------------------===//
// LSLoad
//===----------------------------------------------------------------------===//
namespace {
/// This class represents either a single value that we can load forward or a
/// covering of values that we could load forward from via the introdution of a
/// SILArgument. This enables us to treat both cases the same during our
/// transformations in an abstract way.
class LSLoad : public LSValue {
public:
/// TODO: Add constructor to TinyPtrVector that takes in an individual
LSLoad(LoadInst *NewLoad) : LSValue(NewLoad) {}
/// TODO: Add constructor to TinyPtrVector that takes in an ArrayRef.
LSLoad(SILBasicBlock *NewParentBB, ArrayRef<LoadInst *> NewLoads)
: LSValue(NewParentBB, NewLoads) {}
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// LSStore
//===----------------------------------------------------------------------===//
namespace {
/// This structure represents either a single value or a covering of values that
/// we could use in we can dead store elimination or store forward via the
/// introdution of a SILArgument. This enables us to treat both cases the same
/// during our transformations in an abstract way.
class LSStore : public LSValue {
public:
LSStore(StoreInst *NewStore) : LSValue(NewStore) {}
LSStore(SILBasicBlock *NewParentBB, ArrayRef<StoreInst *> NewStores)
: LSValue(NewParentBB, NewStores) {}
/// Delete the store or set of stores that this LSStore represents.
void deleteDeadValue() {
for (SILInstruction *I : getInsts()) {
I->eraseFromParent();
}
}
/// Returns true if I post dominates all of the stores that we are tracking.
bool postdominates(PostDominanceInfo *PDI, SILInstruction *I) {
for (SILInstruction *Stores : getInsts()) {
if (!PDI->properlyDominates(I, Stores)) {
return false;
}
}
return true;
}
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// LSBBForwarder
//===----------------------------------------------------------------------===//
namespace {
using StoreList = SmallVector<StoreInst *, 8>;
/// The predecessor order in StoreList. We have one StoreInst for each
/// predecessor in StoreList.
using PredOrderInStoreList = SmallVector<SILBasicBlock *, 8>;
/// Map an address to a list of StoreInst, one for each predecessor.
using CoveredStoreMap = llvm::DenseMap<SILValue, StoreList>;
/// State of the load store forwarder in one basic block.
///
/// An invariant of this pass is that a SILValue can only be in one of Stores
/// and Loads. This is enforced by assertions in startTrackingLoad() and
/// startTrackingStore().
class LSBBForwarder {
/// The basic block that we are optimizing.
SILBasicBlock *BB;
/// The current list of store instructions that stored to memory locations
/// that were not read/written to since the store was executed.
llvm::SmallMapVector<SILValue, LSStore, 8> Stores;
// This is a list of LoadInst instructions that reference memory locations
// were not clobbered by instructions that write to memory. In other words
// the SSA value of the load is known to be the same value as the referenced
// pointer. The values in the list are potentially updated on each iteration
// of the loop below.
llvm::SmallMapVector<SILValue, LSLoad, 8> Loads;
public:
LSBBForwarder() = default;
void init(SILBasicBlock *NewBB) {
BB = NewBB;
}
bool optimize(LSContext &Ctx, CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder);
SILBasicBlock *getBB() const { return BB; }
/// Merge in the states of all predecessors.
void mergePredecessorStates(llvm::DenseMap<SILBasicBlock *,
unsigned> &BBToBBIDMap,
std::vector<LSBBForwarder> &BBIDToForwarderMap,
CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder);
/// Clear all state in the BB optimizer.
void clear() {
Stores.clear();
Loads.clear();
}
/// Add this load to our tracking list.
void startTrackingLoad(LoadInst *LI) {
DEBUG(llvm::dbgs() << " Tracking Load: " << *LI);
assert(Stores.find(LI->getOperand()) == Stores.end() &&
"Found new address asked to track in store list already!");
Loads.insert({LI->getOperand(), LSLoad(LI)});
}
/// Add this store to our tracking list.
void startTrackingStore(StoreInst *SI) {
DEBUG(llvm::dbgs() << " Tracking Store: " << *SI);
assert(Loads.find(SI->getDest()) == Loads.end() &&
"Found new address asked to track in load list already!\n");
Stores.insert({SI->getDest(), LSStore(SI)});
}
/// Stop tracking any state related to the address \p Addr.
void stopTrackingAddress(SILValue Addr, CoveredStoreMap &StoreMap) {
DEBUG(llvm::dbgs() << " No Longer Tracking: " << Addr);
Loads.erase(Addr);
Stores.erase(Addr);
StoreMap.erase(Addr);
}
/// Stop tracking any state on the address operaned on by I.
void stopTrackingAddress(SILInstruction *I, CoveredStoreMap &StoreMap) {
SILValue Addr = getAddressForLS(I);
stopTrackingAddress(Addr, StoreMap);
}
/// Delete all instructions that we have mapped to Addr.
void deleteInstsMappedToAddress(SILValue Addr, CoveredStoreMap &StoreMap);
void deleteUntrackedInstruction(SILInstruction *I, CoveredStoreMap &StoreMap);
/// Invalidate any loads that we can not prove that Inst does not write to.
void invalidateAliasingLoads(LSContext &Ctx, SILInstruction *Inst,
CoveredStoreMap &StoreMap);
/// Invalidate our store if Inst writes to the destination location.
void invalidateWriteToStores(LSContext &Ctx, SILInstruction *Inst,
CoveredStoreMap &StoreMap);
/// Invalidate our store if Inst reads from the destination location.
void invalidateReadFromStores(LSContext &Ctx, SILInstruction *Inst,
CoveredStoreMap &StoreMap);
/// Try to prove that SI is a dead store updating all current state. If SI is
/// dead, eliminate it.
bool tryToEliminateDeadStores(LSContext &Ctx, StoreInst *SI,
CoveredStoreMap &StoreMap);
/// Try to find a previously known value that we can forward to LI. This
/// includes from stores and loads.
bool tryToForwardLoad(LSContext &Ctx, LoadInst *LI,
CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder);
private:
/// Merge in the state of an individual predecessor.
void mergePredecessorState(LSBBForwarder &OtherState);
/// Update StoreMap for a given basic block, if there is a reaching store
/// for an address from all the predecessors.
void updateStoreMap(llvm::DenseMap<SILBasicBlock *,
unsigned> &BBToBBIDMap,
std::vector<LSBBForwarder> &BBIDToForwarderMap,
CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder);
bool tryToSubstitutePartialAliasLoad(SILValue PrevAddr, SILValue PrevValue,
LoadInst *LI);
/// Stop tracking all state mapped to Addr and return all instructions
/// associated with Addr in V.
void stopTrackingAddress(SILValue Addr, CoveredStoreMap &StoreMap,
llvm::SmallVectorImpl<SILInstruction *> &V);
};
#ifndef NDEBUG
inline raw_ostream &operator<<(raw_ostream &os,
const std::pair<SILValue, LSLoad> &Value) {
os << "load " << Value.first << " -> " << Value.second;
return os;
}
inline raw_ostream &operator<<(raw_ostream &os,
const std::pair<SILValue, LSStore> &Value) {
os << "store " << Value.first << " -> " << Value.second;
return os;
}
#endif
} // end anonymous namespace
void
LSBBForwarder::
stopTrackingAddress(SILValue Addr, CoveredStoreMap &StoreMap,
llvm::SmallVectorImpl<SILInstruction *> &V) {
auto SIIter = Stores.find(Addr);
if (SIIter != Stores.end()) {
assert((Loads.find(Addr) == Loads.end()) && "An address can never be in "
"both the stores and load lists.");
for (auto *I : SIIter->second.getInsts())
V.push_back(I);
Stores.erase(Addr);
StoreMap.erase(Addr);
return;
}
auto LIIter = Loads.find(Addr);
if (LIIter == Loads.end())
return;
for (auto *I : LIIter->second.getInsts())
V.push_back(I);
Loads.erase(Addr);
}
void
LSBBForwarder::
deleteInstsMappedToAddress(SILValue Addr, CoveredStoreMap &StoreMap) {
llvm::SmallVector<SILInstruction *, 8> InstsToDelete;
stopTrackingAddress(Addr, StoreMap, InstsToDelete);
if (InstsToDelete.empty())
return;
auto UpdateFun = [&](SILInstruction *DeadI) {
if (!isa<LoadInst>(DeadI) && !isa<StoreInst>(DeadI))
return;
stopTrackingAddress(DeadI, StoreMap);
};
for (auto *I : InstsToDelete)
recursivelyDeleteTriviallyDeadInstructions(I, true, UpdateFun);
}
void
LSBBForwarder::
deleteUntrackedInstruction(SILInstruction *I, CoveredStoreMap &StoreMap) {
DEBUG(llvm::dbgs() << " Deleting all instructions recursively from: "
<< *I);
auto UpdateFun = [&](SILInstruction *DeadI) {
if (!isa<LoadInst>(DeadI) && !isa<StoreInst>(DeadI))
return;
stopTrackingAddress(DeadI, StoreMap);
};
recursivelyDeleteTriviallyDeadInstructions(I, true, UpdateFun);
}
void
LSBBForwarder::
invalidateAliasingLoads(LSContext &Ctx, SILInstruction *Inst,
CoveredStoreMap &StoreMap) {
AliasAnalysis *AA = Ctx.getAA();
llvm::SmallVector<SILValue, 4> InvalidatedLoadList;
for (auto &P : Loads)
if (P.second.aliasingWrite(AA, Inst))
InvalidatedLoadList.push_back(P.first);
for (SILValue LIOp : InvalidatedLoadList) {
DEBUG(llvm::dbgs() << " Found an instruction that writes to memory"
" such that a load operand is invalidated:"
<< LIOp);
stopTrackingAddress(LIOp, StoreMap);
}
}
void
LSBBForwarder::
invalidateWriteToStores(LSContext &Ctx, SILInstruction *Inst,
CoveredStoreMap &StoreMap) {
AliasAnalysis *AA = Ctx.getAA();
llvm::SmallVector<SILValue, 4> InvalidatedStoreList;
for (auto &P : Stores)
if (P.second.aliasingWrite(AA, Inst))
InvalidatedStoreList.push_back(P.first);
for (SILValue SIOp : InvalidatedStoreList) {
DEBUG(llvm::dbgs() << " Found an instruction that writes to memory"
" such that a store is invalidated:" << SIOp);
stopTrackingAddress(SIOp, StoreMap);
}
}
void LSBBForwarder::invalidateReadFromStores(LSContext &Ctx,
SILInstruction *Inst,
CoveredStoreMap &StoreMap) {
AliasAnalysis *AA = Ctx.getAA();
llvm::SmallVector<SILValue, 4> InvalidatedStoreList;
for (auto &P : Stores)
if (P.second.aliasingRead(AA, Inst))
InvalidatedStoreList.push_back(P.first);
for (SILValue SIOp : InvalidatedStoreList) {
DEBUG(llvm::dbgs() << " Found an instruction that reads from "
"memory such that a store is invalidated:" << SIOp);
stopTrackingAddress(SIOp, StoreMap);
}
}
bool LSBBForwarder::tryToEliminateDeadStores(LSContext &Ctx, StoreInst *SI,
CoveredStoreMap &StoreMap) {
PostDominanceInfo *PDI = Ctx.getPDI();
AliasAnalysis *AA = Ctx.getAA();
// If we are storing a value that is available in the load list then we
// know that no one clobbered that address and the current store is
// redundant and we can remove it.
if (auto *LdSrc = dyn_cast<LoadInst>(SI->getSrc())) {
// Check that the loaded value is live and that the destination address
// is the same as the loaded address.
SILValue LdSrcOp = LdSrc->getOperand();
auto Iter = Loads.find(LdSrcOp);
if (Iter != Loads.end() && LdSrcOp == SI->getDest()) {
deleteUntrackedInstruction(SI, StoreMap);
NumDeadStores++;
return true;
}
}
// Invalidate any load that we can not prove does not read from the stores
// destination.
invalidateAliasingLoads(Ctx, SI, StoreMap);
// If we are storing to a previously stored address that this store post
// dominates, delete the old store.
llvm::SmallVector<SILValue, 4> StoresToDelete;
llvm::SmallVector<SILValue, 4> StoresToStopTracking;
bool Changed = false;
for (auto &P : Stores) {
if (!P.second.aliasingWrite(AA, SI))
continue;
// We know that the locations might alias. Check whether it is a must alias.
bool IsStoreToSameLocation = AA->isMustAlias(SI->getDest(), P.first);
// If this store may alias but is not known to be to the same location, we
// cannot eliminate it. We need to remove it from the store list and start
// tracking the new store, though.
if (!IsStoreToSameLocation) {
StoresToStopTracking.push_back(P.first);
DEBUG(llvm::dbgs() << " Found an aliasing store... But we don't "
"know that it must alias... Can't remove it but will track it.");
continue;
}
// If this store does not post dominate prev store, we can not eliminate
// it. But do remove prev store from the store list and start tracking the
// new store.
//
// We are only given this if we are being used for multi-bb load store opts
// (when this is required). If we are being used for single-bb load store
// opts, this is not necessary, so skip it.
if (!P.second.postdominates(PDI, SI)) {
StoresToStopTracking.push_back(P.first);
DEBUG(llvm::dbgs() << " Found dead store... That we don't "
"postdominate... Can't remove it but will track it.");
continue;
}
DEBUG(llvm::dbgs() << " Found a dead previous store... Removing...:"
<< P);
Changed = true;
StoresToDelete.push_back(P.first);
NumDeadStores++;
}
for (SILValue SIOp : StoresToDelete)
deleteInstsMappedToAddress(SIOp, StoreMap);
for (SILValue SIOp : StoresToStopTracking)
stopTrackingAddress(SIOp, StoreMap);
// Insert SI into our store list to start tracking.
startTrackingStore(SI);
return Changed;
}
/// See if there is an extract path from LI that we can replace with PrevLI. If
/// we delete all uses of LI this way, delete LI.
bool LSBBForwarder::tryToSubstitutePartialAliasLoad(SILValue PrevLIAddr,
SILValue PrevLIValue,
LoadInst *LI) {
bool Changed = false;
// Since LI and PrevLI partially alias and we know that PrevLI is smaller than
// LI due to where we are in the computation, we compute the address
// projection path from PrevLI's operand to LI's operand.
SILValue UnderlyingPrevLIAddr = getUnderlyingObject(PrevLIAddr);
auto PrevLIPath =
ProjectionPath::getAddrProjectionPath(UnderlyingPrevLIAddr, PrevLIAddr);
if (!PrevLIPath)
return false;
SILValue LIAddr = LI->getOperand();
SILValue UnderlyingLIAddr = getUnderlyingObject(LIAddr);
auto LIPath = ProjectionPath::getAddrProjectionPath(UnderlyingLIAddr, LIAddr);
if (!LIPath)
return false;
// If LIPath matches a prefix of PrevLIPath, return the projection path with
// the prefix removed.
auto P = ProjectionPath::subtractPaths(*PrevLIPath, *LIPath);
if (!P)
return false;
// For all uses of LI, if we can traverse the entire projection path P for
// PrevLI, matching each projection to an extract, replace the final extract
// with the PrevLI.
llvm::SmallVector<SILInstruction *, 8> Tails;
for (auto *Op : LI->getUses()) {
if (P->findMatchingValueProjectionPaths(Op->getUser(), Tails)) {
for (auto *FinalExt : Tails) {
assert(FinalExt->getNumTypes() == 1 && "Expecting only unary types");
SILValue(FinalExt).replaceAllUsesWith(PrevLIValue);
NumForwardedLoads++;
Changed = true;
}
}
Tails.clear();
}
return Changed;
}
/// Add a BBArgument in Dest to combine sources of Stores.
static SILValue fixPhiPredBlocks(SmallVectorImpl<StoreInst *> &Stores,
SmallVectorImpl<SILBasicBlock *> &PredOrder,
SILBasicBlock *Dest) {
SILModule &M = Dest->getModule();
assert(Stores.size() ==
(unsigned)std::distance(Dest->pred_begin(), Dest->pred_end()) &&
"Multiple store forwarding size mismatch");
auto PhiValue = new (M) SILArgument(Dest, Stores[0]->getSrc().getType());
unsigned Id = 0;
for (auto Pred : PredOrder) {
TermInst *TI = Pred->getTerminator();
// This can change the order of predecessors in getPreds.
addArgumentToBranch(Stores[Id++]->getSrc(), Dest, TI);
TI->eraseFromParent();
}
return PhiValue;
}
bool LSBBForwarder::tryToForwardLoad(LSContext &Ctx, LoadInst *LI,
CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder) {
// If we are loading a value that we just stored, forward the stored value.
for (auto &P : Stores) {
SILValue Addr = P.first;
auto CheckResult = ForwardingAnalysis::canForwardAddrToLd(Addr, LI);
if (!CheckResult)
continue;
SILValue Value = P.second.getForwardingValue();
SILValue Result = CheckResult->forwardAddr(Addr, Value, LI);
assert(Result);
DEBUG(llvm::dbgs() << " Forwarding store from: " << *Addr);
SILValue(LI).replaceAllUsesWith(Result);
deleteUntrackedInstruction(LI, StoreMap);
NumForwardedLoads++;
return true;
}
// Search the previous loads and replace the current load or one of the
// current loads uses with one of the previous loads.
for (auto &P : Loads) {
SILValue Addr = P.first;
SILValue Value = P.second.getForwardingValue();
// First Check if LI can be completely replaced by PrevLI or if we can
// construct an extract path from PrevLI's loaded value. The latter occurs
// if PrevLI is a partially aliasing load that completely subsumes LI.
if (SILValue Result = tryToForwardAddressValueToLoad(Addr, Value, LI)) {
DEBUG(llvm::dbgs() << " Replacing with previous load: "
<< *Result);
SILValue(LI).replaceAllUsesWith(Result);
deleteUntrackedInstruction(LI, StoreMap);
NumDupLoads++;
return true;
}
// Otherwise check if LI's operand partially aliases PrevLI's operand. If
// so, see if LI has any uses which could use PrevLI instead of LI
// itself. If LI has no uses after this is completed, delete it and return
// true.
//
// We return true at the end of this if we succeeded to find any uses of LI
// that could be replaced with PrevLI, this means that there could not have
// been a store to LI in between LI and PrevLI since then the store would
// have invalidated PrevLI.
if (Ctx.getAA()->isPartialAlias(LI->getOperand(), Addr)) {
tryToSubstitutePartialAliasLoad(Addr, Value, LI);
}
}
// Check if we can forward from multiple stores.
for (auto I = StoreMap.begin(), E = StoreMap.end(); I != E; I++) {
auto CheckResult = ForwardingAnalysis::canForwardAddrToLd(I->first, LI);
if (!CheckResult)
continue;
DEBUG(llvm::dbgs() << " Checking from: ");
for (auto *SI : I->second) {
DEBUG(llvm::dbgs() << " " << *SI);
(void) SI;
}
// Create a BBargument to merge in multiple stores.
SILValue PhiValue = fixPhiPredBlocks(I->second, PredOrder, BB);
SILValue Result = CheckResult->forwardAddr(I->first,
PhiValue,
LI);
assert(Result && "Forwarding from multiple stores failed!");
DEBUG(llvm::dbgs() << " Forwarding from multiple stores: ");
SILValue(LI).replaceAllUsesWith(Result);
deleteUntrackedInstruction(LI, StoreMap);
NumForwardedLoads++;
return true;
}
startTrackingLoad(LI);
// No partial aliased loads were successfully forwarded. Return false to
// indicate no change.
return false;
}
/// \brief Promote stored values to loads, remove dead stores and merge
/// duplicated loads.
bool LSBBForwarder::optimize(LSContext &Ctx,
CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder) {
auto II = BB->begin(), E = BB->end();
bool Changed = false;
while (II != E) {
SILInstruction *Inst = II++;
DEBUG(llvm::dbgs() << " Visiting: " << *Inst);
// This is a StoreInst. Let's see if we can remove the previous stores.
if (auto *SI = dyn_cast<StoreInst>(Inst)) {
Changed |= tryToEliminateDeadStores(Ctx, SI, StoreMap);
continue;
}
// This is a LoadInst. Let's see if we can find a previous loaded, stored
// value to use instead of this load.
if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) {
Changed |= tryToForwardLoad(Ctx, LI, StoreMap, PredOrder);
continue;
}
// If this instruction has side effects, but is inert from a load store
// perspective, skip it.
if (isLSForwardingInertInstruction(Inst)) {
DEBUG(llvm::dbgs() << " Found inert instruction: " << *Inst);
continue;
}
if (!Inst->mayReadOrWriteMemory()) {
DEBUG(llvm::dbgs() << " Found readnone instruction, does not "
"affect loads and stores.\n");
continue;
}
// All other instructions that read from the memory location of the store
// invalidates the store.
if (Inst->mayReadFromMemory()) {
invalidateReadFromStores(Ctx, Inst, StoreMap);
}
// If we have an instruction that may write to memory and we can not prove
// that it and its operands can not alias a load we have visited, invalidate
// that load.
if (Inst->mayWriteToMemory()) {
// Invalidate any load that we can not prove does not read from one of the
// writing instructions operands.
invalidateAliasingLoads(Ctx, Inst, StoreMap);
// Invalidate our store if Inst writes to the destination location.
invalidateWriteToStores(Ctx, Inst, StoreMap);
}
}
DEBUG(llvm::dbgs() << " Final State\n");
DEBUG(llvm::dbgs() << " Tracking Load Ops:\n";
for (auto &P : Loads) {
llvm::dbgs() << " " << P;
});
DEBUG(llvm::dbgs() << " Tracking Store Ops:\n";
for (auto &P : Stores) {
llvm::dbgs() << " " << P;
});
return Changed;
}
void LSBBForwarder::mergePredecessorState(LSBBForwarder &OtherState) {
// Merge in the predecessor state.
DEBUG(llvm::dbgs() << " Initial Stores:\n");
llvm::SmallVector<SILValue, 8> DeleteList;
for (auto &P : Stores) {
DEBUG(llvm::dbgs() << " " << *P.first);
auto Iter = OtherState.Stores.find(P.first);
if (Iter != OtherState.Stores.end() && P.second == Iter->second)
continue;
DeleteList.push_back(P.first);
}
if (DeleteList.size()) {
DEBUG(llvm::dbgs() << " Stores no longer being tracked:\n");
for (SILValue V : DeleteList) {
// TLDR: We do not remove stores from StoreMap here.
//
// We perform dataflow over self loops. This is done by initializing our
// merging with the original BB state in a later iteration if we
// additionally perform some change in the function. Since we only do this
// with self loops and in all other cases are conservative, this is safe.
Stores.erase(V);
}
DeleteList.clear();
} else {
DEBUG(llvm::dbgs() << " All stores still being tracked!\n");
}
DEBUG(llvm::dbgs() << " Initial Loads:\n");
for (auto &P : Loads) {
DEBUG(llvm::dbgs() << " " << P.first);
auto Iter = OtherState.Loads.find(P.first);
if (Iter != OtherState.Loads.end() && P.second == Iter->second)
continue;
DeleteList.push_back(P.first);
}
if (DeleteList.size()) {
DEBUG(llvm::dbgs() << " Loads no longer being tracked:\n");
for (SILValue V : DeleteList) {
Loads.erase(V);
}
} else {
DEBUG(llvm::dbgs() << " All loads still being tracked!\n");
}
}
/// Update StoreMap for a given basic block, if there is a reaching store
/// for an address from all the predecessors.
void LSBBForwarder::
updateStoreMap(llvm::DenseMap<SILBasicBlock *,
unsigned> &BBToBBIDMap,
std::vector<LSBBForwarder> &BBIDToForwarderMap,
CoveredStoreMap &StoreMap, PredOrderInStoreList &PredOrder) {
if (std::distance(BB->pred_begin(), BB->pred_end()) <= 1)
return;
bool FirstPred = true;
for (auto Pred : BB->getPreds()) {
// Bail out if one of the predecessors has a terminator that we currently
// do not handle.
if (!isa<CondBranchInst>(Pred->getTerminator()) &&
!isa<BranchInst>(Pred->getTerminator())) {
StoreMap.clear();
return;
}
PredOrder.push_back(Pred);
auto I = BBToBBIDMap.find(Pred);
if (I == BBToBBIDMap.end()) {
StoreMap.clear();
return;
}
LSBBForwarder &Other = BBIDToForwarderMap[I->second];
// Calculate SILValues that are stored once in this predecessor.
llvm::DenseMap<SILValue, StoreInst *> StoredMapOfThisPred;
llvm::SmallSet<SILValue, 16> StoredValuesOfThisPred;
for (auto &P : Other.Stores) {
if (!StoredValuesOfThisPred.count(P.first)) {
// Assert to make sure to update this when we transition covering stores
// to use LSValues.
assert(P.second.getInsts().size() == 1);
auto *SI = cast<StoreInst>(P.second.getInsts()[0]);
StoredMapOfThisPred[P.first] = SI;
} else {
StoredMapOfThisPred.erase(P.first);
}
StoredValuesOfThisPred.insert(P.first);
}
if (FirstPred) {
// Update StoreMap with stores from the first predecessor.
for (auto I = StoredMapOfThisPred.begin(), E = StoredMapOfThisPred.end();
I != E; I++) {
StoreMap[I->first].push_back(I->second);
DEBUG(llvm::dbgs() << " Updating StoreMap bb" <<
Pred->getDebugID() << ": " << I->first << " " <<
*I->second);
}
} else {
// Merge in stores from other predecessors.
for (auto I = StoreMap.begin(), E = StoreMap.end(); I != E;) {
SILValue Current = I->first;
if (!StoredMapOfThisPred.count(Current) ||
I->second[0]->getSrc().getType() !=
StoredMapOfThisPred[Current]->getSrc().getType()) {
DEBUG(llvm::dbgs() << " Removing from StoreMap: " << Current);
I++; // Move to the next before erasing the current.
StoreMap.erase(Current);
}
else {
I->second.push_back(StoredMapOfThisPred[Current]);
DEBUG(llvm::dbgs() << " Updating StoreMap bb" <<
Pred->getDebugID() << ": " << Current <<
" " << *StoredMapOfThisPred[Current]);
I++;
}
}
}
FirstPred = false;
}
}
void
LSBBForwarder::
mergePredecessorStates(llvm::DenseMap<SILBasicBlock *,
unsigned> &BBToBBIDMap,
std::vector<LSBBForwarder> &BBIDToForwarderMap,
CoveredStoreMap &StoreMap,
PredOrderInStoreList &PredOrder) {
// Clear the state if the basic block has no predecessor.
if (BB->getPreds().empty()) {
clear();
return;
}
// Update StoreMap. Do this before updating Stores since we need the states
// at the end of the basic blocks.
updateStoreMap(BBToBBIDMap, BBIDToForwarderMap, StoreMap, PredOrder);
bool HasAtLeastOnePred = false;
// If we have a self cycle, we keep the old state and merge in states
// of other predecessors. Otherwise, we initialize the state with the first
// predecessor's state and merge in states of other predecessors.
SILBasicBlock *TheBB = getBB();
bool HasSelfCycle = std::any_of(BB->pred_begin(), BB->pred_end(),
[&TheBB](SILBasicBlock *Pred) -> bool {
return Pred == TheBB;
});
// For each predecessor of BB...
for (auto Pred : BB->getPreds()) {
// Lookup the BBState associated with the predecessor and merge the
// predecessor in.
auto I = BBToBBIDMap.find(Pred);
// If we can not lookup the BBID then the BB was not in the post order
// implying that it is unreachable. LLVM will ensure that the BB is removed
// if we do not reach it at the SIL level. Since it is unreachable, ignore
// it.
if (I == BBToBBIDMap.end())
continue;
LSBBForwarder &Other = BBIDToForwarderMap[I->second];
// If we have not had at least one predecessor, initialize LSBBForwarder
// with the state of the initial predecessor.
// If BB is also a predecessor of itself, we should not initialize.
if (!HasAtLeastOnePred && !HasSelfCycle) {
DEBUG(llvm::dbgs() << " Initializing with pred: " << I->second
<< "\n");
Stores = Other.Stores;
Loads = Other.Loads;
DEBUG(llvm::dbgs() << " Tracking Loads:\n";
for (auto &P : Loads) {
llvm::dbgs() << " " << P;
});
DEBUG(llvm::dbgs() << " Tracking Stores:\n";
for (auto &P : Stores) {
llvm::dbgs() << " " << P;
});
} else if (Pred != BB) {
DEBUG(llvm::dbgs() << " Merging with pred bb" << Pred->getDebugID() <<
"\n");
mergePredecessorState(Other);
}
HasAtLeastOnePred = true;
}
for (auto &P : Stores) {
DEBUG(llvm::dbgs() << " Removing from StoreMap: " << P);
StoreMap.erase(P.first);
}
}
//===----------------------------------------------------------------------===//
// LSContext Implementation
//===----------------------------------------------------------------------===//
static inline unsigned roundPostOrderSize(PostOrderAnalysis::reverse_range R) {
unsigned PostOrderSize = std::distance(R.begin(), R.end());
// NextPowerOf2 operates on uint64_t, so we can not overflow since our input
// is a 32 bit value. But we need to make sure if the next power of 2 is
// greater than the representable UINT_MAX, we just pass in (1 << 31) if the
// next power of 2 is (1 << 32).
uint64_t SizeRoundedToPow2 = llvm::NextPowerOf2(PostOrderSize);
if (SizeRoundedToPow2 > uint64_t(UINT_MAX))
return 1 << 31;
return unsigned(SizeRoundedToPow2);
}
LSContext::LSContext(AliasAnalysis *AA, PostDominanceInfo *PDI,
PostOrderAnalysis::reverse_range RPOT)
: AA(AA), PDI(PDI), ReversePostOrder(RPOT),
BBToBBIDMap(roundPostOrderSize(RPOT)),
BBIDToForwarderMap(roundPostOrderSize(RPOT)) {
for (SILBasicBlock *BB : ReversePostOrder) {
unsigned count = BBToBBIDMap.size();
BBToBBIDMap[BB] = count;
BBIDToForwarderMap[count].init(BB);
}
}
bool
LSContext::runIteration() {
bool Changed = false;
for (SILBasicBlock *BB : ReversePostOrder) {
auto IDIter = BBToBBIDMap.find(BB);
assert(IDIter != BBToBBIDMap.end() && "We just constructed this!?");
unsigned ID = IDIter->second;
LSBBForwarder &Forwarder = BBIDToForwarderMap[ID];
assert(Forwarder.getBB() == BB && "We just constructed this!?");
DEBUG(llvm::dbgs() << "Visiting bb" << BB->getDebugID() << "\n");
CoveredStoreMap StoreMap;
PredOrderInStoreList PredOrder;
// Merge the predecessors. After merging, LSBBForwarder now contains
// lists of stores|loads that reach the beginning of the basic block
// along all paths.
Forwarder.mergePredecessorStates(BBToBBIDMap, BBIDToForwarderMap,
StoreMap, PredOrder);
// Remove dead stores, merge duplicate loads, and forward stores to
// loads. We also update lists of stores|loads to reflect the end
// of the basic block.
Changed |= Forwarder.optimize(*this, StoreMap, PredOrder);
}
return Changed;
}
//===----------------------------------------------------------------------===//
// Top Level Entry Point
//===----------------------------------------------------------------------===//
namespace {
class GlobalLoadStoreOpts : public SILFunctionTransform {
/// The entry point to the transformation.
void run() override {
SILFunction *F = getFunction();
DEBUG(llvm::dbgs() << "***** Load Store Elimination on function: "
<< F->getName() << " *****\n");
auto *AA = PM->getAnalysis<AliasAnalysis>();
auto *POTA = PM->getAnalysis<PostOrderAnalysis>();
auto *PDA = PM->getAnalysis<PostDominanceAnalysis>();
auto *PDI = PDA->get(F);
LSContext Ctx(AA, PDI, POTA->getReversePostOrder(F));
bool Changed = false;
while (Ctx.runIteration())
Changed = true;
if (Changed)
invalidateAnalysis(SILAnalysis::PreserveKind::ProgramFlow);
}
StringRef getName() override { return "SIL Load Store Opts"; }
};
} // end anonymous namespace
SILTransform *swift::createGlobalLoadStoreOpts() {
return new GlobalLoadStoreOpts();
}