#include "llvm/Analysis/MemorySSA.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CFGPrinter.h"
#include "llvm/Analysis/IteratedDominanceFrontier.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Use.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <iterator>
#include <memory>
#include <utility>
using namespace llvm;
#define DEBUG_TYPE "memoryssa"
static cl::opt<std::string>
DotCFGMSSA("dot-cfg-mssa",
cl::value_desc("file name for generated dot file"),
cl::desc("file name for generated dot file"), cl::init(""));
INITIALIZE_PASS_BEGIN(MemorySSAWrapperPass, "memoryssa", "Memory SSA", false,
true)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_END(MemorySSAWrapperPass, "memoryssa", "Memory SSA", false,
true)
INITIALIZE_PASS_BEGIN(MemorySSAPrinterLegacyPass, "print-memoryssa",
"Memory SSA Printer", false, false)
INITIALIZE_PASS_DEPENDENCY(MemorySSAWrapperPass)
INITIALIZE_PASS_END(MemorySSAPrinterLegacyPass, "print-memoryssa",
"Memory SSA Printer", false, false)
static cl::opt<unsigned> MaxCheckLimit(
"memssa-check-limit", cl::Hidden, cl::init(100),
cl::desc("The maximum number of stores/phis MemorySSA"
"will consider trying to walk past (default = 100)"));
#ifdef EXPENSIVE_CHECKS
bool llvm::VerifyMemorySSA = true;
#else
bool llvm::VerifyMemorySSA = false;
#endif
static cl::opt<bool, true>
VerifyMemorySSAX("verify-memoryssa", cl::location(VerifyMemorySSA),
cl::Hidden, cl::desc("Enable verification of MemorySSA."));
const static char LiveOnEntryStr[] = "liveOnEntry";
namespace {
class MemorySSAAnnotatedWriter : public AssemblyAnnotationWriter {
const MemorySSA *MSSA;
public:
MemorySSAAnnotatedWriter(const MemorySSA *M) : MSSA(M) {}
void emitBasicBlockStartAnnot(const BasicBlock *BB,
formatted_raw_ostream &OS) override {
if (MemoryAccess *MA = MSSA->getMemoryAccess(BB))
OS << "; " << *MA << "\n";
}
void emitInstructionAnnot(const Instruction *I,
formatted_raw_ostream &OS) override {
if (MemoryAccess *MA = MSSA->getMemoryAccess(I))
OS << "; " << *MA << "\n";
}
};
class MemorySSAWalkerAnnotatedWriter : public AssemblyAnnotationWriter {
MemorySSA *MSSA;
MemorySSAWalker *Walker;
public:
MemorySSAWalkerAnnotatedWriter(MemorySSA *M)
: MSSA(M), Walker(M->getWalker()) {}
void emitBasicBlockStartAnnot(const BasicBlock *BB,
formatted_raw_ostream &OS) override {
if (MemoryAccess *MA = MSSA->getMemoryAccess(BB))
OS << "; " << *MA << "\n";
}
void emitInstructionAnnot(const Instruction *I,
formatted_raw_ostream &OS) override {
if (MemoryAccess *MA = MSSA->getMemoryAccess(I)) {
MemoryAccess *Clobber = Walker->getClobberingMemoryAccess(MA);
OS << "; " << *MA;
if (Clobber) {
OS << " - clobbered by ";
if (MSSA->isLiveOnEntryDef(Clobber))
OS << LiveOnEntryStr;
else
OS << *Clobber;
}
OS << "\n";
}
}
};
}
namespace {
class MemoryLocOrCall {
public:
bool IsCall = false;
MemoryLocOrCall(MemoryUseOrDef *MUD)
: MemoryLocOrCall(MUD->getMemoryInst()) {}
MemoryLocOrCall(const MemoryUseOrDef *MUD)
: MemoryLocOrCall(MUD->getMemoryInst()) {}
MemoryLocOrCall(Instruction *Inst) {
if (auto *C = dyn_cast<CallBase>(Inst)) {
IsCall = true;
Call = C;
} else {
IsCall = false;
if (!isa<FenceInst>(Inst))
Loc = MemoryLocation::get(Inst);
}
}
explicit MemoryLocOrCall(const MemoryLocation &Loc) : Loc(Loc) {}
const CallBase *getCall() const {
assert(IsCall);
return Call;
}
MemoryLocation getLoc() const {
assert(!IsCall);
return Loc;
}
bool operator==(const MemoryLocOrCall &Other) const {
if (IsCall != Other.IsCall)
return false;
if (!IsCall)
return Loc == Other.Loc;
if (Call->getCalledOperand() != Other.Call->getCalledOperand())
return false;
return Call->arg_size() == Other.Call->arg_size() &&
std::equal(Call->arg_begin(), Call->arg_end(),
Other.Call->arg_begin());
}
private:
union {
const CallBase *Call;
MemoryLocation Loc;
};
};
}
namespace llvm {
template <> struct DenseMapInfo<MemoryLocOrCall> {
static inline MemoryLocOrCall getEmptyKey() {
return MemoryLocOrCall(DenseMapInfo<MemoryLocation>::getEmptyKey());
}
static inline MemoryLocOrCall getTombstoneKey() {
return MemoryLocOrCall(DenseMapInfo<MemoryLocation>::getTombstoneKey());
}
static unsigned getHashValue(const MemoryLocOrCall &MLOC) {
if (!MLOC.IsCall)
return hash_combine(
MLOC.IsCall,
DenseMapInfo<MemoryLocation>::getHashValue(MLOC.getLoc()));
hash_code hash =
hash_combine(MLOC.IsCall, DenseMapInfo<const Value *>::getHashValue(
MLOC.getCall()->getCalledOperand()));
for (const Value *Arg : MLOC.getCall()->args())
hash = hash_combine(hash, DenseMapInfo<const Value *>::getHashValue(Arg));
return hash;
}
static bool isEqual(const MemoryLocOrCall &LHS, const MemoryLocOrCall &RHS) {
return LHS == RHS;
}
};
}
static bool areLoadsReorderable(const LoadInst *Use,
const LoadInst *MayClobber) {
bool VolatileUse = Use->isVolatile();
bool VolatileClobber = MayClobber->isVolatile();
if (VolatileUse && VolatileClobber)
return false;
bool SeqCstUse = Use->getOrdering() == AtomicOrdering::SequentiallyConsistent;
bool MayClobberIsAcquire = isAtLeastOrStrongerThan(MayClobber->getOrdering(),
AtomicOrdering::Acquire);
return !(SeqCstUse || MayClobberIsAcquire);
}
namespace {
struct ClobberAlias {
bool IsClobber;
Optional<AliasResult> AR;
};
}
template <typename AliasAnalysisType>
static ClobberAlias
instructionClobbersQuery(const MemoryDef *MD, const MemoryLocation &UseLoc,
const Instruction *UseInst, AliasAnalysisType &AA) {
Instruction *DefInst = MD->getMemoryInst();
assert(DefInst && "Defining instruction not actually an instruction");
Optional<AliasResult> AR;
if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(DefInst)) {
switch (II->getIntrinsicID()) {
case Intrinsic::invariant_start:
case Intrinsic::invariant_end:
case Intrinsic::assume:
case Intrinsic::experimental_noalias_scope_decl:
case Intrinsic::pseudoprobe:
return {false, AliasResult(AliasResult::NoAlias)};
case Intrinsic::dbg_addr:
case Intrinsic::dbg_declare:
case Intrinsic::dbg_label:
case Intrinsic::dbg_value:
llvm_unreachable("debuginfo shouldn't have associated defs!");
default:
break;
}
}
if (auto *CB = dyn_cast_or_null<CallBase>(UseInst)) {
ModRefInfo I = AA.getModRefInfo(DefInst, CB);
AR = isMustSet(I) ? AliasResult::MustAlias : AliasResult::MayAlias;
return {isModOrRefSet(I), AR};
}
if (auto *DefLoad = dyn_cast<LoadInst>(DefInst))
if (auto *UseLoad = dyn_cast_or_null<LoadInst>(UseInst))
return {!areLoadsReorderable(UseLoad, DefLoad),
AliasResult(AliasResult::MayAlias)};
ModRefInfo I = AA.getModRefInfo(DefInst, UseLoc);
AR = isMustSet(I) ? AliasResult::MustAlias : AliasResult::MayAlias;
return {isModSet(I), AR};
}
template <typename AliasAnalysisType>
static ClobberAlias instructionClobbersQuery(MemoryDef *MD,
const MemoryUseOrDef *MU,
const MemoryLocOrCall &UseMLOC,
AliasAnalysisType &AA) {
if (UseMLOC.IsCall)
return instructionClobbersQuery(MD, MemoryLocation(), MU->getMemoryInst(),
AA);
return instructionClobbersQuery(MD, UseMLOC.getLoc(), MU->getMemoryInst(),
AA);
}
bool MemorySSAUtil::defClobbersUseOrDef(MemoryDef *MD, const MemoryUseOrDef *MU,
AliasAnalysis &AA) {
return instructionClobbersQuery(MD, MU, MemoryLocOrCall(MU), AA).IsClobber;
}
namespace {
struct UpwardsMemoryQuery {
bool IsCall = false;
MemoryLocation StartingLoc;
const Instruction *Inst = nullptr;
const MemoryAccess *OriginalAccess = nullptr;
Optional<AliasResult> AR = AliasResult(AliasResult::MayAlias);
bool SkipSelfAccess = false;
UpwardsMemoryQuery() = default;
UpwardsMemoryQuery(const Instruction *Inst, const MemoryAccess *Access)
: IsCall(isa<CallBase>(Inst)), Inst(Inst), OriginalAccess(Access) {
if (!IsCall)
StartingLoc = MemoryLocation::get(Inst);
}
};
}
template <typename AliasAnalysisType>
static bool isUseTriviallyOptimizableToLiveOnEntry(AliasAnalysisType &AA,
const Instruction *I) {
if (auto *LI = dyn_cast<LoadInst>(I))
return I->hasMetadata(LLVMContext::MD_invariant_load) ||
AA.pointsToConstantMemory(MemoryLocation::get(LI));
return false;
}
template <typename AliasAnalysisType>
LLVM_ATTRIBUTE_UNUSED static void
checkClobberSanity(const MemoryAccess *Start, MemoryAccess *ClobberAt,
const MemoryLocation &StartLoc, const MemorySSA &MSSA,
const UpwardsMemoryQuery &Query, AliasAnalysisType &AA,
bool AllowImpreciseClobber = false) {
assert(MSSA.dominates(ClobberAt, Start) && "Clobber doesn't dominate start?");
if (MSSA.isLiveOnEntryDef(Start)) {
assert(MSSA.isLiveOnEntryDef(ClobberAt) &&
"liveOnEntry must clobber itself");
return;
}
bool FoundClobber = false;
DenseSet<ConstMemoryAccessPair> VisitedPhis;
SmallVector<ConstMemoryAccessPair, 8> Worklist;
Worklist.emplace_back(Start, StartLoc);
while (!Worklist.empty()) {
auto MAP = Worklist.pop_back_val();
if (!VisitedPhis.insert(MAP).second)
continue;
for (const auto *MA : def_chain(MAP.first)) {
if (MA == ClobberAt) {
if (const auto *MD = dyn_cast<MemoryDef>(MA)) {
FoundClobber = FoundClobber || MSSA.isLiveOnEntryDef(MD);
if (!FoundClobber) {
ClobberAlias CA =
instructionClobbersQuery(MD, MAP.second, Query.Inst, AA);
if (CA.IsClobber) {
FoundClobber = true;
}
}
}
break;
}
assert(!MSSA.isLiveOnEntryDef(MA) && "Hit liveOnEntry before clobber?");
if (const auto *MD = dyn_cast<MemoryDef>(MA)) {
if (MD == Start)
continue;
assert(!instructionClobbersQuery(MD, MAP.second, Query.Inst, AA)
.IsClobber &&
"Found clobber before reaching ClobberAt!");
continue;
}
if (const auto *MU = dyn_cast<MemoryUse>(MA)) {
(void)MU;
assert (MU == Start &&
"Can only find use in def chain if Start is a use");
continue;
}
assert(isa<MemoryPhi>(MA));
for (auto ItB = upward_defs_begin(
{const_cast<MemoryAccess *>(MA), MAP.second},
MSSA.getDomTree()),
ItE = upward_defs_end();
ItB != ItE; ++ItB)
if (MSSA.getDomTree().isReachableFromEntry(ItB.getPhiArgBlock()))
Worklist.emplace_back(*ItB);
}
}
if (AllowImpreciseClobber)
return;
assert((isa<MemoryPhi>(ClobberAt) || FoundClobber) &&
"ClobberAt never acted as a clobber");
}
namespace {
template <class AliasAnalysisType> class ClobberWalker {
using ListIndex = unsigned;
struct DefPath {
MemoryLocation Loc;
MemoryAccess *First;
MemoryAccess *Last;
Optional<ListIndex> Previous;
DefPath(const MemoryLocation &Loc, MemoryAccess *First, MemoryAccess *Last,
Optional<ListIndex> Previous)
: Loc(Loc), First(First), Last(Last), Previous(Previous) {}
DefPath(const MemoryLocation &Loc, MemoryAccess *Init,
Optional<ListIndex> Previous)
: DefPath(Loc, Init, Init, Previous) {}
};
const MemorySSA &MSSA;
AliasAnalysisType &AA;
DominatorTree &DT;
UpwardsMemoryQuery *Query;
unsigned *UpwardWalkLimit;
SmallVector<DefPath, 32> Paths;
DenseSet<ConstMemoryAccessPair> VisitedPhis;
bool PerformedPhiTranslation = false;
const MemoryAccess *getWalkTarget(const MemoryPhi *From) const {
assert(From->getNumOperands() && "Phi with no operands?");
BasicBlock *BB = From->getBlock();
MemoryAccess *Result = MSSA.getLiveOnEntryDef();
DomTreeNode *Node = DT.getNode(BB);
while ((Node = Node->getIDom())) {
auto *Defs = MSSA.getBlockDefs(Node->getBlock());
if (Defs)
return &*Defs->rbegin();
}
return Result;
}
struct UpwardsWalkResult {
MemoryAccess *Result;
bool IsKnownClobber;
Optional<AliasResult> AR;
};
UpwardsWalkResult
walkToPhiOrClobber(DefPath &Desc, const MemoryAccess *StopAt = nullptr,
const MemoryAccess *SkipStopAt = nullptr) const {
assert(!isa<MemoryUse>(Desc.Last) && "Uses don't exist in my world");
assert(UpwardWalkLimit && "Need a valid walk limit");
bool LimitAlreadyReached = false;
if (!*UpwardWalkLimit) {
*UpwardWalkLimit = 1;
LimitAlreadyReached = true;
}
for (MemoryAccess *Current : def_chain(Desc.Last)) {
Desc.Last = Current;
if (Current == StopAt || Current == SkipStopAt)
return {Current, false, AliasResult(AliasResult::MayAlias)};
if (auto *MD = dyn_cast<MemoryDef>(Current)) {
if (MSSA.isLiveOnEntryDef(MD))
return {MD, true, AliasResult(AliasResult::MustAlias)};
if (!--*UpwardWalkLimit)
return {Current, true, AliasResult(AliasResult::MayAlias)};
ClobberAlias CA =
instructionClobbersQuery(MD, Desc.Loc, Query->Inst, AA);
if (CA.IsClobber)
return {MD, true, CA.AR};
}
}
if (LimitAlreadyReached)
*UpwardWalkLimit = 0;
assert(isa<MemoryPhi>(Desc.Last) &&
"Ended at a non-clobber that's not a phi?");
return {Desc.Last, false, AliasResult(AliasResult::MayAlias)};
}
void addSearches(MemoryPhi *Phi, SmallVectorImpl<ListIndex> &PausedSearches,
ListIndex PriorNode) {
auto UpwardDefsBegin = upward_defs_begin({Phi, Paths[PriorNode].Loc}, DT,
&PerformedPhiTranslation);
auto UpwardDefs = make_range(UpwardDefsBegin, upward_defs_end());
for (const MemoryAccessPair &P : UpwardDefs) {
PausedSearches.push_back(Paths.size());
Paths.emplace_back(P.second, P.first, PriorNode);
}
}
struct TerminatedPath {
MemoryAccess *Clobber;
ListIndex LastNode;
};
Optional<TerminatedPath>
getBlockingAccess(const MemoryAccess *StopWhere,
SmallVectorImpl<ListIndex> &PausedSearches,
SmallVectorImpl<ListIndex> &NewPaused,
SmallVectorImpl<TerminatedPath> &Terminated) {
assert(!PausedSearches.empty() && "No searches to continue?");
while (!PausedSearches.empty()) {
ListIndex PathIndex = PausedSearches.pop_back_val();
DefPath &Node = Paths[PathIndex];
if (!VisitedPhis.insert({Node.Last, Node.Loc}).second) {
if (PerformedPhiTranslation) {
TerminatedPath Term{Node.Last, PathIndex};
return Term;
}
continue;
}
const MemoryAccess *SkipStopWhere = nullptr;
if (Query->SkipSelfAccess && Node.Loc == Query->StartingLoc) {
assert(isa<MemoryDef>(Query->OriginalAccess));
SkipStopWhere = Query->OriginalAccess;
}
UpwardsWalkResult Res = walkToPhiOrClobber(Node,
StopWhere,
SkipStopWhere);
if (Res.IsKnownClobber) {
assert(Res.Result != StopWhere && Res.Result != SkipStopWhere);
TerminatedPath Term{Res.Result, PathIndex};
if (!MSSA.dominates(Res.Result, StopWhere))
return Term;
Terminated.push_back(Term);
continue;
}
if (Res.Result == StopWhere || Res.Result == SkipStopWhere) {
if (Res.Result != SkipStopWhere)
NewPaused.push_back(PathIndex);
continue;
}
assert(!MSSA.isLiveOnEntryDef(Res.Result) && "liveOnEntry is a clobber");
addSearches(cast<MemoryPhi>(Res.Result), PausedSearches, PathIndex);
}
return None;
}
template <typename T, typename Walker>
struct generic_def_path_iterator
: public iterator_facade_base<generic_def_path_iterator<T, Walker>,
std::forward_iterator_tag, T *> {
generic_def_path_iterator() = default;
generic_def_path_iterator(Walker *W, ListIndex N) : W(W), N(N) {}
T &operator*() const { return curNode(); }
generic_def_path_iterator &operator++() {
N = curNode().Previous;
return *this;
}
bool operator==(const generic_def_path_iterator &O) const {
if (N.has_value() != O.N.has_value())
return false;
return !N || *N == *O.N;
}
private:
T &curNode() const { return W->Paths[*N]; }
Walker *W = nullptr;
Optional<ListIndex> N = None;
};
using def_path_iterator = generic_def_path_iterator<DefPath, ClobberWalker>;
using const_def_path_iterator =
generic_def_path_iterator<const DefPath, const ClobberWalker>;
iterator_range<def_path_iterator> def_path(ListIndex From) {
return make_range(def_path_iterator(this, From), def_path_iterator());
}
iterator_range<const_def_path_iterator> const_def_path(ListIndex From) const {
return make_range(const_def_path_iterator(this, From),
const_def_path_iterator());
}
struct OptznResult {
TerminatedPath PrimaryClobber;
SmallVector<TerminatedPath, 4> OtherClobbers;
};
ListIndex defPathIndex(const DefPath &N) const {
const DefPath *NP = &N;
assert(!Paths.empty() && NP >= &Paths.front() && NP <= &Paths.back() &&
"Out of bounds DefPath!");
return NP - &Paths.front();
}
OptznResult tryOptimizePhi(MemoryPhi *Phi, MemoryAccess *Start,
const MemoryLocation &Loc) {
assert(Paths.empty() && VisitedPhis.empty() && !PerformedPhiTranslation &&
"Reset the optimization state.");
Paths.emplace_back(Loc, Start, Phi, None);
auto PriorPathsSize = Paths.size();
SmallVector<ListIndex, 16> PausedSearches;
SmallVector<ListIndex, 8> NewPaused;
SmallVector<TerminatedPath, 4> TerminatedPaths;
addSearches(Phi, PausedSearches, 0);
auto MoveDominatedPathToEnd = [&](SmallVectorImpl<TerminatedPath> &Paths) {
assert(!Paths.empty() && "Need a path to move");
auto Dom = Paths.begin();
for (auto I = std::next(Dom), E = Paths.end(); I != E; ++I)
if (!MSSA.dominates(I->Clobber, Dom->Clobber))
Dom = I;
auto Last = Paths.end() - 1;
if (Last != Dom)
std::iter_swap(Last, Dom);
};
MemoryPhi *Current = Phi;
while (true) {
assert(!MSSA.isLiveOnEntryDef(Current) &&
"liveOnEntry wasn't treated as a clobber?");
const auto *Target = getWalkTarget(Current);
assert(all_of(TerminatedPaths, [&](const TerminatedPath &P) {
return MSSA.dominates(P.Clobber, Target);
}));
if (Optional<TerminatedPath> Blocker = getBlockingAccess(
Target, PausedSearches, NewPaused, TerminatedPaths)) {
auto Iter = find_if(def_path(Blocker->LastNode), [&](const DefPath &N) {
return defPathIndex(N) < PriorPathsSize;
});
assert(Iter != def_path_iterator());
DefPath &CurNode = *Iter;
assert(CurNode.Last == Current);
TerminatedPath Result{CurNode.Last, defPathIndex(CurNode)};
return {Result, {}};
}
if (NewPaused.empty()) {
MoveDominatedPathToEnd(TerminatedPaths);
TerminatedPath Result = TerminatedPaths.pop_back_val();
return {Result, std::move(TerminatedPaths)};
}
MemoryAccess *DefChainEnd = nullptr;
SmallVector<TerminatedPath, 4> Clobbers;
for (ListIndex Paused : NewPaused) {
UpwardsWalkResult WR = walkToPhiOrClobber(Paths[Paused]);
if (WR.IsKnownClobber)
Clobbers.push_back({WR.Result, Paused});
else
DefChainEnd = WR.Result;
}
if (!TerminatedPaths.empty()) {
if (!DefChainEnd)
for (auto *MA : def_chain(const_cast<MemoryAccess *>(Target)))
DefChainEnd = MA;
assert(DefChainEnd && "Failed to find dominating phi/liveOnEntry");
const BasicBlock *ChainBB = DefChainEnd->getBlock();
for (const TerminatedPath &TP : TerminatedPaths) {
if (DT.dominates(ChainBB, TP.Clobber->getBlock()))
Clobbers.push_back(TP);
}
}
if (!Clobbers.empty()) {
MoveDominatedPathToEnd(Clobbers);
TerminatedPath Result = Clobbers.pop_back_val();
return {Result, std::move(Clobbers)};
}
assert(all_of(NewPaused,
[&](ListIndex I) { return Paths[I].Last == DefChainEnd; }));
auto *DefChainPhi = cast<MemoryPhi>(DefChainEnd);
PriorPathsSize = Paths.size();
PausedSearches.clear();
for (ListIndex I : NewPaused)
addSearches(DefChainPhi, PausedSearches, I);
NewPaused.clear();
Current = DefChainPhi;
}
}
void verifyOptResult(const OptznResult &R) const {
assert(all_of(R.OtherClobbers, [&](const TerminatedPath &P) {
return MSSA.dominates(P.Clobber, R.PrimaryClobber.Clobber);
}));
}
void resetPhiOptznState() {
Paths.clear();
VisitedPhis.clear();
PerformedPhiTranslation = false;
}
public:
ClobberWalker(const MemorySSA &MSSA, AliasAnalysisType &AA, DominatorTree &DT)
: MSSA(MSSA), AA(AA), DT(DT) {}
AliasAnalysisType *getAA() { return &AA; }
MemoryAccess *findClobber(MemoryAccess *Start, UpwardsMemoryQuery &Q,
unsigned &UpWalkLimit) {
Query = &Q;
UpwardWalkLimit = &UpWalkLimit;
if (!UpWalkLimit)
UpWalkLimit++;
MemoryAccess *Current = Start;
if (auto *MU = dyn_cast<MemoryUse>(Start))
Current = MU->getDefiningAccess();
DefPath FirstDesc(Q.StartingLoc, Current, Current, None);
UpwardsWalkResult WalkResult = walkToPhiOrClobber(FirstDesc);
MemoryAccess *Result;
if (WalkResult.IsKnownClobber) {
Result = WalkResult.Result;
Q.AR = WalkResult.AR;
} else {
OptznResult OptRes = tryOptimizePhi(cast<MemoryPhi>(FirstDesc.Last),
Current, Q.StartingLoc);
verifyOptResult(OptRes);
resetPhiOptznState();
Result = OptRes.PrimaryClobber.Clobber;
}
#ifdef EXPENSIVE_CHECKS
if (!Q.SkipSelfAccess && *UpwardWalkLimit > 0)
checkClobberSanity(Current, Result, Q.StartingLoc, MSSA, Q, AA);
#endif
return Result;
}
};
struct RenamePassData {
DomTreeNode *DTN;
DomTreeNode::const_iterator ChildIt;
MemoryAccess *IncomingVal;
RenamePassData(DomTreeNode *D, DomTreeNode::const_iterator It,
MemoryAccess *M)
: DTN(D), ChildIt(It), IncomingVal(M) {}
void swap(RenamePassData &RHS) {
std::swap(DTN, RHS.DTN);
std::swap(ChildIt, RHS.ChildIt);
std::swap(IncomingVal, RHS.IncomingVal);
}
};
}
namespace llvm {
template <class AliasAnalysisType> class MemorySSA::ClobberWalkerBase {
ClobberWalker<AliasAnalysisType> Walker;
MemorySSA *MSSA;
public:
ClobberWalkerBase(MemorySSA *M, AliasAnalysisType *A, DominatorTree *D)
: Walker(*M, *A, *D), MSSA(M) {}
MemoryAccess *getClobberingMemoryAccessBase(MemoryAccess *,
const MemoryLocation &,
unsigned &);
MemoryAccess *getClobberingMemoryAccessBase(MemoryAccess *, unsigned &, bool,
bool UseInvariantGroup = true);
};
template <class AliasAnalysisType>
class MemorySSA::CachingWalker final : public MemorySSAWalker {
ClobberWalkerBase<AliasAnalysisType> *Walker;
public:
CachingWalker(MemorySSA *M, ClobberWalkerBase<AliasAnalysisType> *W)
: MemorySSAWalker(M), Walker(W) {}
~CachingWalker() override = default;
using MemorySSAWalker::getClobberingMemoryAccess;
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA, unsigned &UWL) {
return Walker->getClobberingMemoryAccessBase(MA, UWL, false);
}
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA,
const MemoryLocation &Loc,
unsigned &UWL) {
return Walker->getClobberingMemoryAccessBase(MA, Loc, UWL);
}
MemoryAccess *getClobberingMemoryAccessWithoutInvariantGroup(MemoryAccess *MA,
unsigned &UWL) {
return Walker->getClobberingMemoryAccessBase(MA, UWL, false, false);
}
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA) override {
unsigned UpwardWalkLimit = MaxCheckLimit;
return getClobberingMemoryAccess(MA, UpwardWalkLimit);
}
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA,
const MemoryLocation &Loc) override {
unsigned UpwardWalkLimit = MaxCheckLimit;
return getClobberingMemoryAccess(MA, Loc, UpwardWalkLimit);
}
void invalidateInfo(MemoryAccess *MA) override {
if (auto *MUD = dyn_cast<MemoryUseOrDef>(MA))
MUD->resetOptimized();
}
};
template <class AliasAnalysisType>
class MemorySSA::SkipSelfWalker final : public MemorySSAWalker {
ClobberWalkerBase<AliasAnalysisType> *Walker;
public:
SkipSelfWalker(MemorySSA *M, ClobberWalkerBase<AliasAnalysisType> *W)
: MemorySSAWalker(M), Walker(W) {}
~SkipSelfWalker() override = default;
using MemorySSAWalker::getClobberingMemoryAccess;
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA, unsigned &UWL) {
return Walker->getClobberingMemoryAccessBase(MA, UWL, true);
}
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA,
const MemoryLocation &Loc,
unsigned &UWL) {
return Walker->getClobberingMemoryAccessBase(MA, Loc, UWL);
}
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA) override {
unsigned UpwardWalkLimit = MaxCheckLimit;
return getClobberingMemoryAccess(MA, UpwardWalkLimit);
}
MemoryAccess *getClobberingMemoryAccess(MemoryAccess *MA,
const MemoryLocation &Loc) override {
unsigned UpwardWalkLimit = MaxCheckLimit;
return getClobberingMemoryAccess(MA, Loc, UpwardWalkLimit);
}
void invalidateInfo(MemoryAccess *MA) override {
if (auto *MUD = dyn_cast<MemoryUseOrDef>(MA))
MUD->resetOptimized();
}
};
}
void MemorySSA::renameSuccessorPhis(BasicBlock *BB, MemoryAccess *IncomingVal,
bool RenameAllUses) {
for (const BasicBlock *S : successors(BB)) {
auto It = PerBlockAccesses.find(S);
if (It == PerBlockAccesses.end() || !isa<MemoryPhi>(It->second->front()))
continue;
AccessList *Accesses = It->second.get();
auto *Phi = cast<MemoryPhi>(&Accesses->front());
if (RenameAllUses) {
bool ReplacementDone = false;
for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I)
if (Phi->getIncomingBlock(I) == BB) {
Phi->setIncomingValue(I, IncomingVal);
ReplacementDone = true;
}
(void) ReplacementDone;
assert(ReplacementDone && "Incomplete phi during partial rename");
} else
Phi->addIncoming(IncomingVal, BB);
}
}
MemoryAccess *MemorySSA::renameBlock(BasicBlock *BB, MemoryAccess *IncomingVal,
bool RenameAllUses) {
auto It = PerBlockAccesses.find(BB);
if (It != PerBlockAccesses.end()) {
AccessList *Accesses = It->second.get();
for (MemoryAccess &L : *Accesses) {
if (MemoryUseOrDef *MUD = dyn_cast<MemoryUseOrDef>(&L)) {
if (MUD->getDefiningAccess() == nullptr || RenameAllUses)
MUD->setDefiningAccess(IncomingVal);
if (isa<MemoryDef>(&L))
IncomingVal = &L;
} else {
IncomingVal = &L;
}
}
}
return IncomingVal;
}
void MemorySSA::renamePass(DomTreeNode *Root, MemoryAccess *IncomingVal,
SmallPtrSetImpl<BasicBlock *> &Visited,
bool SkipVisited, bool RenameAllUses) {
assert(Root && "Trying to rename accesses in an unreachable block");
SmallVector<RenamePassData, 32> WorkStack;
bool AlreadyVisited = !Visited.insert(Root->getBlock()).second;
if (SkipVisited && AlreadyVisited)
return;
IncomingVal = renameBlock(Root->getBlock(), IncomingVal, RenameAllUses);
renameSuccessorPhis(Root->getBlock(), IncomingVal, RenameAllUses);
WorkStack.push_back({Root, Root->begin(), IncomingVal});
while (!WorkStack.empty()) {
DomTreeNode *Node = WorkStack.back().DTN;
DomTreeNode::const_iterator ChildIt = WorkStack.back().ChildIt;
IncomingVal = WorkStack.back().IncomingVal;
if (ChildIt == Node->end()) {
WorkStack.pop_back();
} else {
DomTreeNode *Child = *ChildIt;
++WorkStack.back().ChildIt;
BasicBlock *BB = Child->getBlock();
AlreadyVisited = !Visited.insert(BB).second;
if (SkipVisited && AlreadyVisited) {
if (auto *BlockDefs = getWritableBlockDefs(BB))
IncomingVal = &*BlockDefs->rbegin();
} else
IncomingVal = renameBlock(BB, IncomingVal, RenameAllUses);
renameSuccessorPhis(BB, IncomingVal, RenameAllUses);
WorkStack.push_back({Child, Child->begin(), IncomingVal});
}
}
}
void MemorySSA::markUnreachableAsLiveOnEntry(BasicBlock *BB) {
assert(!DT->isReachableFromEntry(BB) &&
"Reachable block found while handling unreachable blocks");
for (const BasicBlock *S : successors(BB)) {
if (!DT->isReachableFromEntry(S))
continue;
auto It = PerBlockAccesses.find(S);
if (It == PerBlockAccesses.end() || !isa<MemoryPhi>(It->second->front()))
continue;
AccessList *Accesses = It->second.get();
auto *Phi = cast<MemoryPhi>(&Accesses->front());
Phi->addIncoming(LiveOnEntryDef.get(), BB);
}
auto It = PerBlockAccesses.find(BB);
if (It == PerBlockAccesses.end())
return;
auto &Accesses = It->second;
for (auto AI = Accesses->begin(), AE = Accesses->end(); AI != AE;) {
auto Next = std::next(AI);
if (auto *UseOrDef = dyn_cast<MemoryUseOrDef>(AI))
UseOrDef->setDefiningAccess(LiveOnEntryDef.get());
else
Accesses->erase(AI);
AI = Next;
}
}
MemorySSA::MemorySSA(Function &Func, AliasAnalysis *AA, DominatorTree *DT)
: DT(DT), F(Func), LiveOnEntryDef(nullptr), Walker(nullptr),
SkipWalker(nullptr) {
assert(AA && "No alias analysis?");
BatchAAResults BatchAA(*AA);
buildMemorySSA(BatchAA);
this->AA = AA;
getWalker();
}
MemorySSA::~MemorySSA() {
for (const auto &Pair : PerBlockAccesses)
for (MemoryAccess &MA : *Pair.second)
MA.dropAllReferences();
}
MemorySSA::AccessList *MemorySSA::getOrCreateAccessList(const BasicBlock *BB) {
auto Res = PerBlockAccesses.insert(std::make_pair(BB, nullptr));
if (Res.second)
Res.first->second = std::make_unique<AccessList>();
return Res.first->second.get();
}
MemorySSA::DefsList *MemorySSA::getOrCreateDefsList(const BasicBlock *BB) {
auto Res = PerBlockDefs.insert(std::make_pair(BB, nullptr));
if (Res.second)
Res.first->second = std::make_unique<DefsList>();
return Res.first->second.get();
}
namespace llvm {
class MemorySSA::OptimizeUses {
public:
OptimizeUses(MemorySSA *MSSA, CachingWalker<BatchAAResults> *Walker,
BatchAAResults *BAA, DominatorTree *DT)
: MSSA(MSSA), Walker(Walker), AA(BAA), DT(DT) {}
void optimizeUses();
private:
struct MemlocStackInfo {
unsigned long StackEpoch;
unsigned long PopEpoch;
unsigned long LowerBound;
const BasicBlock *LowerBoundBlock;
unsigned long LastKill;
bool LastKillValid;
Optional<AliasResult> AR;
};
void optimizeUsesInBlock(const BasicBlock *, unsigned long &, unsigned long &,
SmallVectorImpl<MemoryAccess *> &,
DenseMap<MemoryLocOrCall, MemlocStackInfo> &);
MemorySSA *MSSA;
CachingWalker<BatchAAResults> *Walker;
BatchAAResults *AA;
DominatorTree *DT;
};
}
void MemorySSA::OptimizeUses::optimizeUsesInBlock(
const BasicBlock *BB, unsigned long &StackEpoch, unsigned long &PopEpoch,
SmallVectorImpl<MemoryAccess *> &VersionStack,
DenseMap<MemoryLocOrCall, MemlocStackInfo> &LocStackInfo) {
MemorySSA::AccessList *Accesses = MSSA->getWritableBlockAccesses(BB);
if (Accesses == nullptr)
return;
while (true) {
assert(
!VersionStack.empty() &&
"Version stack should have liveOnEntry sentinel dominating everything");
BasicBlock *BackBlock = VersionStack.back()->getBlock();
if (DT->dominates(BackBlock, BB))
break;
while (VersionStack.back()->getBlock() == BackBlock)
VersionStack.pop_back();
++PopEpoch;
}
for (MemoryAccess &MA : *Accesses) {
auto *MU = dyn_cast<MemoryUse>(&MA);
if (!MU) {
VersionStack.push_back(&MA);
++StackEpoch;
continue;
}
if (MU->isOptimized())
continue;
if (isUseTriviallyOptimizableToLiveOnEntry(*AA, MU->getMemoryInst())) {
MU->setDefiningAccess(MSSA->getLiveOnEntryDef(), true, None);
continue;
}
MemoryLocOrCall UseMLOC(MU);
auto &LocInfo = LocStackInfo[UseMLOC];
if (LocInfo.PopEpoch != PopEpoch) {
LocInfo.PopEpoch = PopEpoch;
LocInfo.StackEpoch = StackEpoch;
if (LocInfo.LowerBoundBlock && LocInfo.LowerBoundBlock != BB &&
!DT->dominates(LocInfo.LowerBoundBlock, BB)) {
LocInfo.LowerBound = 0;
LocInfo.LowerBoundBlock = VersionStack[0]->getBlock();
LocInfo.LastKillValid = false;
}
} else if (LocInfo.StackEpoch != StackEpoch) {
LocInfo.PopEpoch = PopEpoch;
LocInfo.StackEpoch = StackEpoch;
}
if (!LocInfo.LastKillValid) {
LocInfo.LastKill = VersionStack.size() - 1;
LocInfo.LastKillValid = true;
LocInfo.AR = AliasResult::MayAlias;
}
assert(LocInfo.LowerBound < VersionStack.size() &&
"Lower bound out of range");
assert(LocInfo.LastKill < VersionStack.size() &&
"Last kill info out of range");
unsigned long UpperBound = VersionStack.size() - 1;
if (UpperBound - LocInfo.LowerBound > MaxCheckLimit) {
LLVM_DEBUG(dbgs() << "MemorySSA skipping optimization of " << *MU << " ("
<< *(MU->getMemoryInst()) << ")"
<< " because there are "
<< UpperBound - LocInfo.LowerBound
<< " stores to disambiguate\n");
LocInfo.LastKillValid = false;
continue;
}
bool FoundClobberResult = false;
unsigned UpwardWalkLimit = MaxCheckLimit;
while (UpperBound > LocInfo.LowerBound) {
if (isa<MemoryPhi>(VersionStack[UpperBound])) {
MemoryAccess *Result =
Walker->getClobberingMemoryAccessWithoutInvariantGroup(
MU, UpwardWalkLimit);
while (VersionStack[UpperBound] != Result) {
assert(UpperBound != 0);
--UpperBound;
}
FoundClobberResult = true;
break;
}
MemoryDef *MD = cast<MemoryDef>(VersionStack[UpperBound]);
ClobberAlias CA = instructionClobbersQuery(MD, MU, UseMLOC, *AA);
if (CA.IsClobber) {
FoundClobberResult = true;
LocInfo.AR = CA.AR;
break;
}
--UpperBound;
}
if (FoundClobberResult || UpperBound < LocInfo.LastKill) {
if (MSSA->isLiveOnEntryDef(VersionStack[UpperBound]))
LocInfo.AR = None;
MU->setDefiningAccess(VersionStack[UpperBound], true, LocInfo.AR);
LocInfo.LastKill = UpperBound;
} else {
MU->setDefiningAccess(VersionStack[LocInfo.LastKill], true, LocInfo.AR);
}
LocInfo.LowerBound = VersionStack.size() - 1;
LocInfo.LowerBoundBlock = BB;
}
}
void MemorySSA::OptimizeUses::optimizeUses() {
SmallVector<MemoryAccess *, 16> VersionStack;
DenseMap<MemoryLocOrCall, MemlocStackInfo> LocStackInfo;
VersionStack.push_back(MSSA->getLiveOnEntryDef());
unsigned long StackEpoch = 1;
unsigned long PopEpoch = 1;
for (const auto *DomNode : depth_first(DT->getRootNode()))
optimizeUsesInBlock(DomNode->getBlock(), StackEpoch, PopEpoch, VersionStack,
LocStackInfo);
}
void MemorySSA::placePHINodes(
const SmallPtrSetImpl<BasicBlock *> &DefiningBlocks) {
ForwardIDFCalculator IDFs(*DT);
IDFs.setDefiningBlocks(DefiningBlocks);
SmallVector<BasicBlock *, 32> IDFBlocks;
IDFs.calculate(IDFBlocks);
for (auto &BB : IDFBlocks)
createMemoryPhi(BB);
}
void MemorySSA::buildMemorySSA(BatchAAResults &BAA) {
BasicBlock &StartingPoint = F.getEntryBlock();
LiveOnEntryDef.reset(new MemoryDef(F.getContext(), nullptr, nullptr,
&StartingPoint, NextID++));
SmallPtrSet<BasicBlock *, 32> DefiningBlocks;
for (BasicBlock &B : F) {
bool InsertIntoDef = false;
AccessList *Accesses = nullptr;
DefsList *Defs = nullptr;
for (Instruction &I : B) {
MemoryUseOrDef *MUD = createNewAccess(&I, &BAA);
if (!MUD)
continue;
if (!Accesses)
Accesses = getOrCreateAccessList(&B);
Accesses->push_back(MUD);
if (isa<MemoryDef>(MUD)) {
InsertIntoDef = true;
if (!Defs)
Defs = getOrCreateDefsList(&B);
Defs->push_back(*MUD);
}
}
if (InsertIntoDef)
DefiningBlocks.insert(&B);
}
placePHINodes(DefiningBlocks);
SmallPtrSet<BasicBlock *, 16> Visited;
renamePass(DT->getRootNode(), LiveOnEntryDef.get(), Visited);
for (auto &BB : F)
if (!Visited.count(&BB))
markUnreachableAsLiveOnEntry(&BB);
}
MemorySSAWalker *MemorySSA::getWalker() { return getWalkerImpl(); }
MemorySSA::CachingWalker<AliasAnalysis> *MemorySSA::getWalkerImpl() {
if (Walker)
return Walker.get();
if (!WalkerBase)
WalkerBase =
std::make_unique<ClobberWalkerBase<AliasAnalysis>>(this, AA, DT);
Walker =
std::make_unique<CachingWalker<AliasAnalysis>>(this, WalkerBase.get());
return Walker.get();
}
MemorySSAWalker *MemorySSA::getSkipSelfWalker() {
if (SkipWalker)
return SkipWalker.get();
if (!WalkerBase)
WalkerBase =
std::make_unique<ClobberWalkerBase<AliasAnalysis>>(this, AA, DT);
SkipWalker =
std::make_unique<SkipSelfWalker<AliasAnalysis>>(this, WalkerBase.get());
return SkipWalker.get();
}
void MemorySSA::insertIntoListsForBlock(MemoryAccess *NewAccess,
const BasicBlock *BB,
InsertionPlace Point) {
auto *Accesses = getOrCreateAccessList(BB);
if (Point == Beginning) {
if (isa<MemoryPhi>(NewAccess)) {
Accesses->push_front(NewAccess);
auto *Defs = getOrCreateDefsList(BB);
Defs->push_front(*NewAccess);
} else {
auto AI = find_if_not(
*Accesses, [](const MemoryAccess &MA) { return isa<MemoryPhi>(MA); });
Accesses->insert(AI, NewAccess);
if (!isa<MemoryUse>(NewAccess)) {
auto *Defs = getOrCreateDefsList(BB);
auto DI = find_if_not(
*Defs, [](const MemoryAccess &MA) { return isa<MemoryPhi>(MA); });
Defs->insert(DI, *NewAccess);
}
}
} else {
Accesses->push_back(NewAccess);
if (!isa<MemoryUse>(NewAccess)) {
auto *Defs = getOrCreateDefsList(BB);
Defs->push_back(*NewAccess);
}
}
BlockNumberingValid.erase(BB);
}
void MemorySSA::insertIntoListsBefore(MemoryAccess *What, const BasicBlock *BB,
AccessList::iterator InsertPt) {
auto *Accesses = getWritableBlockAccesses(BB);
bool WasEnd = InsertPt == Accesses->end();
Accesses->insert(AccessList::iterator(InsertPt), What);
if (!isa<MemoryUse>(What)) {
auto *Defs = getOrCreateDefsList(BB);
if (WasEnd) {
Defs->push_back(*What);
} else if (isa<MemoryDef>(InsertPt)) {
Defs->insert(InsertPt->getDefsIterator(), *What);
} else {
while (InsertPt != Accesses->end() && !isa<MemoryDef>(InsertPt))
++InsertPt;
if (InsertPt == Accesses->end())
Defs->push_back(*What);
else
Defs->insert(InsertPt->getDefsIterator(), *What);
}
}
BlockNumberingValid.erase(BB);
}
void MemorySSA::prepareForMoveTo(MemoryAccess *What, BasicBlock *BB) {
removeFromLists(What, false);
if (auto *MD = dyn_cast<MemoryDef>(What))
MD->resetOptimized();
What->setBlock(BB);
}
void MemorySSA::moveTo(MemoryUseOrDef *What, BasicBlock *BB,
AccessList::iterator Where) {
prepareForMoveTo(What, BB);
insertIntoListsBefore(What, BB, Where);
}
void MemorySSA::moveTo(MemoryAccess *What, BasicBlock *BB,
InsertionPlace Point) {
if (isa<MemoryPhi>(What)) {
assert(Point == Beginning &&
"Can only move a Phi at the beginning of the block");
ValueToMemoryAccess.erase(What->getBlock());
bool Inserted = ValueToMemoryAccess.insert({BB, What}).second;
(void)Inserted;
assert(Inserted && "Cannot move a Phi to a block that already has one");
}
prepareForMoveTo(What, BB);
insertIntoListsForBlock(What, BB, Point);
}
MemoryPhi *MemorySSA::createMemoryPhi(BasicBlock *BB) {
assert(!getMemoryAccess(BB) && "MemoryPhi already exists for this BB");
MemoryPhi *Phi = new MemoryPhi(BB->getContext(), BB, NextID++);
insertIntoListsForBlock(Phi, BB, Beginning);
ValueToMemoryAccess[BB] = Phi;
return Phi;
}
MemoryUseOrDef *MemorySSA::createDefinedAccess(Instruction *I,
MemoryAccess *Definition,
const MemoryUseOrDef *Template,
bool CreationMustSucceed) {
assert(!isa<PHINode>(I) && "Cannot create a defined access for a PHI");
MemoryUseOrDef *NewAccess = createNewAccess(I, AA, Template);
if (CreationMustSucceed)
assert(NewAccess != nullptr && "Tried to create a memory access for a "
"non-memory touching instruction");
if (NewAccess) {
assert((!Definition || !isa<MemoryUse>(Definition)) &&
"A use cannot be a defining access");
NewAccess->setDefiningAccess(Definition);
}
return NewAccess;
}
static inline bool isOrdered(const Instruction *I) {
if (auto *SI = dyn_cast<StoreInst>(I)) {
if (!SI->isUnordered())
return true;
} else if (auto *LI = dyn_cast<LoadInst>(I)) {
if (!LI->isUnordered())
return true;
}
return false;
}
template <typename AliasAnalysisType>
MemoryUseOrDef *MemorySSA::createNewAccess(Instruction *I,
AliasAnalysisType *AAP,
const MemoryUseOrDef *Template) {
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
switch (II->getIntrinsicID()) {
default:
break;
case Intrinsic::assume:
case Intrinsic::experimental_noalias_scope_decl:
case Intrinsic::pseudoprobe:
return nullptr;
}
}
if (!I->mayReadFromMemory() && !I->mayWriteToMemory())
return nullptr;
bool Def, Use;
if (Template) {
Def = isa<MemoryDef>(Template);
Use = isa<MemoryUse>(Template);
#if !defined(NDEBUG)
ModRefInfo ModRef = AAP->getModRefInfo(I, None);
bool DefCheck, UseCheck;
DefCheck = isModSet(ModRef) || isOrdered(I);
UseCheck = isRefSet(ModRef);
assert(Def == DefCheck && (Def || Use == UseCheck) && "Invalid template");
#endif
} else {
ModRefInfo ModRef = AAP->getModRefInfo(I, None);
Def = isModSet(ModRef) || isOrdered(I);
Use = isRefSet(ModRef);
}
if (!Def && !Use)
return nullptr;
MemoryUseOrDef *MUD;
if (Def)
MUD = new MemoryDef(I->getContext(), nullptr, I, I->getParent(), NextID++);
else
MUD = new MemoryUse(I->getContext(), nullptr, I, I->getParent());
ValueToMemoryAccess[I] = MUD;
return MUD;
}
void MemorySSA::removeFromLookups(MemoryAccess *MA) {
assert(MA->use_empty() &&
"Trying to remove memory access that still has uses");
BlockNumbering.erase(MA);
if (auto *MUD = dyn_cast<MemoryUseOrDef>(MA))
MUD->setDefiningAccess(nullptr);
if (!isa<MemoryUse>(MA))
getWalker()->invalidateInfo(MA);
Value *MemoryInst;
if (const auto *MUD = dyn_cast<MemoryUseOrDef>(MA))
MemoryInst = MUD->getMemoryInst();
else
MemoryInst = MA->getBlock();
auto VMA = ValueToMemoryAccess.find(MemoryInst);
if (VMA->second == MA)
ValueToMemoryAccess.erase(VMA);
}
void MemorySSA::removeFromLists(MemoryAccess *MA, bool ShouldDelete) {
BasicBlock *BB = MA->getBlock();
if (!isa<MemoryUse>(MA)) {
auto DefsIt = PerBlockDefs.find(BB);
std::unique_ptr<DefsList> &Defs = DefsIt->second;
Defs->remove(*MA);
if (Defs->empty())
PerBlockDefs.erase(DefsIt);
}
auto AccessIt = PerBlockAccesses.find(BB);
std::unique_ptr<AccessList> &Accesses = AccessIt->second;
if (ShouldDelete)
Accesses->erase(MA);
else
Accesses->remove(MA);
if (Accesses->empty()) {
PerBlockAccesses.erase(AccessIt);
BlockNumberingValid.erase(BB);
}
}
void MemorySSA::print(raw_ostream &OS) const {
MemorySSAAnnotatedWriter Writer(this);
F.print(OS, &Writer);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void MemorySSA::dump() const { print(dbgs()); }
#endif
void MemorySSA::verifyMemorySSA(VerificationLevel VL) const {
#if !defined(NDEBUG) && defined(EXPENSIVE_CHECKS)
VL = VerificationLevel::Full;
#endif
#ifndef NDEBUG
verifyOrderingDominationAndDefUses(F, VL);
verifyDominationNumbers(F);
if (VL == VerificationLevel::Full)
verifyPrevDefInPhis(F);
#endif
}
void MemorySSA::verifyPrevDefInPhis(Function &F) const {
for (const BasicBlock &BB : F) {
if (MemoryPhi *Phi = getMemoryAccess(&BB)) {
for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) {
auto *Pred = Phi->getIncomingBlock(I);
auto *IncAcc = Phi->getIncomingValue(I);
if (auto *DTNode = DT->getNode(Pred)) {
while (DTNode) {
if (auto *DefList = getBlockDefs(DTNode->getBlock())) {
auto *LastAcc = &*(--DefList->end());
assert(LastAcc == IncAcc &&
"Incorrect incoming access into phi.");
(void)IncAcc;
(void)LastAcc;
break;
}
DTNode = DTNode->getIDom();
}
} else {
}
}
}
}
}
void MemorySSA::verifyDominationNumbers(const Function &F) const {
if (BlockNumberingValid.empty())
return;
SmallPtrSet<const BasicBlock *, 16> ValidBlocks = BlockNumberingValid;
for (const BasicBlock &BB : F) {
if (!ValidBlocks.count(&BB))
continue;
ValidBlocks.erase(&BB);
const AccessList *Accesses = getBlockAccesses(&BB);
if (!Accesses)
continue;
unsigned long LastNumber = 0;
for (const MemoryAccess &MA : *Accesses) {
auto ThisNumberIter = BlockNumbering.find(&MA);
assert(ThisNumberIter != BlockNumbering.end() &&
"MemoryAccess has no domination number in a valid block!");
unsigned long ThisNumber = ThisNumberIter->second;
assert(ThisNumber > LastNumber &&
"Domination numbers should be strictly increasing!");
(void)LastNumber;
LastNumber = ThisNumber;
}
}
assert(ValidBlocks.empty() &&
"All valid BasicBlocks should exist in F -- dangling pointers?");
}
void MemorySSA::verifyOrderingDominationAndDefUses(Function &F,
VerificationLevel VL) const {
SmallVector<MemoryAccess *, 32> ActualAccesses;
SmallVector<MemoryAccess *, 32> ActualDefs;
for (BasicBlock &B : F) {
const AccessList *AL = getBlockAccesses(&B);
const auto *DL = getBlockDefs(&B);
MemoryPhi *Phi = getMemoryAccess(&B);
if (Phi) {
ActualAccesses.push_back(Phi);
ActualDefs.push_back(Phi);
for (const Use &U : Phi->uses()) {
assert(dominates(Phi, U) && "Memory PHI does not dominate it's uses");
(void)U;
}
if (VL == VerificationLevel::Full) {
assert(Phi->getNumOperands() == static_cast<unsigned>(std::distance(
pred_begin(&B), pred_end(&B))) &&
"Incomplete MemoryPhi Node");
for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) {
verifyUseInDefs(Phi->getIncomingValue(I), Phi);
assert(is_contained(predecessors(&B), Phi->getIncomingBlock(I)) &&
"Incoming phi block not a block predecessor");
}
}
}
for (Instruction &I : B) {
MemoryUseOrDef *MA = getMemoryAccess(&I);
assert((!MA || (AL && (isa<MemoryUse>(MA) || DL))) &&
"We have memory affecting instructions "
"in this block but they are not in the "
"access list or defs list");
if (MA) {
ActualAccesses.push_back(MA);
if (MemoryAccess *MD = dyn_cast<MemoryDef>(MA)) {
ActualDefs.push_back(MA);
for (const Use &U : MD->uses()) {
assert(dominates(MD, U) &&
"Memory Def does not dominate it's uses");
(void)U;
}
}
if (VL == VerificationLevel::Full)
verifyUseInDefs(MA->getDefiningAccess(), MA);
}
}
if (!AL && !DL)
continue;
assert(AL->size() == ActualAccesses.size() &&
"We don't have the same number of accesses in the block as on the "
"access list");
assert((DL || ActualDefs.size() == 0) &&
"Either we should have a defs list, or we should have no defs");
assert((!DL || DL->size() == ActualDefs.size()) &&
"We don't have the same number of defs in the block as on the "
"def list");
auto ALI = AL->begin();
auto AAI = ActualAccesses.begin();
while (ALI != AL->end() && AAI != ActualAccesses.end()) {
assert(&*ALI == *AAI && "Not the same accesses in the same order");
++ALI;
++AAI;
}
ActualAccesses.clear();
if (DL) {
auto DLI = DL->begin();
auto ADI = ActualDefs.begin();
while (DLI != DL->end() && ADI != ActualDefs.end()) {
assert(&*DLI == *ADI && "Not the same defs in the same order");
++DLI;
++ADI;
}
}
ActualDefs.clear();
}
}
void MemorySSA::verifyUseInDefs(MemoryAccess *Def, MemoryAccess *Use) const {
if (!Def)
assert(isLiveOnEntryDef(Use) &&
"Null def but use not point to live on entry def");
else
assert(is_contained(Def->users(), Use) &&
"Did not find use in def's use list");
}
void MemorySSA::renumberBlock(const BasicBlock *B) const {
unsigned long CurrentNumber = 0;
const AccessList *AL = getBlockAccesses(B);
assert(AL != nullptr && "Asking to renumber an empty block");
for (const auto &I : *AL)
BlockNumbering[&I] = ++CurrentNumber;
BlockNumberingValid.insert(B);
}
bool MemorySSA::locallyDominates(const MemoryAccess *Dominator,
const MemoryAccess *Dominatee) const {
const BasicBlock *DominatorBlock = Dominator->getBlock();
assert((DominatorBlock == Dominatee->getBlock()) &&
"Asking for local domination when accesses are in different blocks!");
if (Dominatee == Dominator)
return true;
if (isLiveOnEntryDef(Dominatee))
return false;
if (isLiveOnEntryDef(Dominator))
return true;
if (!BlockNumberingValid.count(DominatorBlock))
renumberBlock(DominatorBlock);
unsigned long DominatorNum = BlockNumbering.lookup(Dominator);
assert(DominatorNum != 0 && "Block was not numbered properly");
unsigned long DominateeNum = BlockNumbering.lookup(Dominatee);
assert(DominateeNum != 0 && "Block was not numbered properly");
return DominatorNum < DominateeNum;
}
bool MemorySSA::dominates(const MemoryAccess *Dominator,
const MemoryAccess *Dominatee) const {
if (Dominator == Dominatee)
return true;
if (isLiveOnEntryDef(Dominatee))
return false;
if (Dominator->getBlock() != Dominatee->getBlock())
return DT->dominates(Dominator->getBlock(), Dominatee->getBlock());
return locallyDominates(Dominator, Dominatee);
}
bool MemorySSA::dominates(const MemoryAccess *Dominator,
const Use &Dominatee) const {
if (MemoryPhi *MP = dyn_cast<MemoryPhi>(Dominatee.getUser())) {
BasicBlock *UseBB = MP->getIncomingBlock(Dominatee);
if (UseBB != Dominator->getBlock())
return DT->dominates(Dominator->getBlock(), UseBB);
return locallyDominates(Dominator, cast<MemoryAccess>(Dominatee));
}
return dominates(Dominator, cast<MemoryAccess>(Dominatee.getUser()));
}
void MemorySSA::ensureOptimizedUses() {
if (IsOptimized)
return;
BatchAAResults BatchAA(*AA);
ClobberWalkerBase<BatchAAResults> WalkerBase(this, &BatchAA, DT);
CachingWalker<BatchAAResults> WalkerLocal(this, &WalkerBase);
OptimizeUses(this, &WalkerLocal, &BatchAA, DT).optimizeUses();
IsOptimized = true;
}
void MemoryAccess::print(raw_ostream &OS) const {
switch (getValueID()) {
case MemoryPhiVal: return static_cast<const MemoryPhi *>(this)->print(OS);
case MemoryDefVal: return static_cast<const MemoryDef *>(this)->print(OS);
case MemoryUseVal: return static_cast<const MemoryUse *>(this)->print(OS);
}
llvm_unreachable("invalid value id");
}
void MemoryDef::print(raw_ostream &OS) const {
MemoryAccess *UO = getDefiningAccess();
auto printID = [&OS](MemoryAccess *A) {
if (A && A->getID())
OS << A->getID();
else
OS << LiveOnEntryStr;
};
OS << getID() << " = MemoryDef(";
printID(UO);
OS << ")";
if (isOptimized()) {
OS << "->";
printID(getOptimized());
if (Optional<AliasResult> AR = getOptimizedAccessType())
OS << " " << *AR;
}
}
void MemoryPhi::print(raw_ostream &OS) const {
ListSeparator LS(",");
OS << getID() << " = MemoryPhi(";
for (const auto &Op : operands()) {
BasicBlock *BB = getIncomingBlock(Op);
MemoryAccess *MA = cast<MemoryAccess>(Op);
OS << LS << '{';
if (BB->hasName())
OS << BB->getName();
else
BB->printAsOperand(OS, false);
OS << ',';
if (unsigned ID = MA->getID())
OS << ID;
else
OS << LiveOnEntryStr;
OS << '}';
}
OS << ')';
}
void MemoryUse::print(raw_ostream &OS) const {
MemoryAccess *UO = getDefiningAccess();
OS << "MemoryUse(";
if (UO && UO->getID())
OS << UO->getID();
else
OS << LiveOnEntryStr;
OS << ')';
if (Optional<AliasResult> AR = getOptimizedAccessType())
OS << " " << *AR;
}
void MemoryAccess::dump() const {
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
print(dbgs());
dbgs() << "\n";
#endif
}
char MemorySSAPrinterLegacyPass::ID = 0;
MemorySSAPrinterLegacyPass::MemorySSAPrinterLegacyPass() : FunctionPass(ID) {
initializeMemorySSAPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
}
void MemorySSAPrinterLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequired<MemorySSAWrapperPass>();
}
class DOTFuncMSSAInfo {
private:
const Function &F;
MemorySSAAnnotatedWriter MSSAWriter;
public:
DOTFuncMSSAInfo(const Function &F, MemorySSA &MSSA)
: F(F), MSSAWriter(&MSSA) {}
const Function *getFunction() { return &F; }
MemorySSAAnnotatedWriter &getWriter() { return MSSAWriter; }
};
namespace llvm {
template <>
struct GraphTraits<DOTFuncMSSAInfo *> : public GraphTraits<const BasicBlock *> {
static NodeRef getEntryNode(DOTFuncMSSAInfo *CFGInfo) {
return &(CFGInfo->getFunction()->getEntryBlock());
}
using nodes_iterator = pointer_iterator<Function::const_iterator>;
static nodes_iterator nodes_begin(DOTFuncMSSAInfo *CFGInfo) {
return nodes_iterator(CFGInfo->getFunction()->begin());
}
static nodes_iterator nodes_end(DOTFuncMSSAInfo *CFGInfo) {
return nodes_iterator(CFGInfo->getFunction()->end());
}
static size_t size(DOTFuncMSSAInfo *CFGInfo) {
return CFGInfo->getFunction()->size();
}
};
template <>
struct DOTGraphTraits<DOTFuncMSSAInfo *> : public DefaultDOTGraphTraits {
DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {}
static std::string getGraphName(DOTFuncMSSAInfo *CFGInfo) {
return "MSSA CFG for '" + CFGInfo->getFunction()->getName().str() +
"' function";
}
std::string getNodeLabel(const BasicBlock *Node, DOTFuncMSSAInfo *CFGInfo) {
return DOTGraphTraits<DOTFuncInfo *>::getCompleteNodeLabel(
Node, nullptr,
[CFGInfo](raw_string_ostream &OS, const BasicBlock &BB) -> void {
BB.print(OS, &CFGInfo->getWriter(), true, true);
},
[](std::string &S, unsigned &I, unsigned Idx) -> void {
std::string Str = S.substr(I, Idx - I);
StringRef SR = Str;
if (SR.count(" = MemoryDef(") || SR.count(" = MemoryPhi(") ||
SR.count("MemoryUse("))
return;
DOTGraphTraits<DOTFuncInfo *>::eraseComment(S, I, Idx);
});
}
static std::string getEdgeSourceLabel(const BasicBlock *Node,
const_succ_iterator I) {
return DOTGraphTraits<DOTFuncInfo *>::getEdgeSourceLabel(Node, I);
}
std::string getEdgeAttributes(const BasicBlock *Node, const_succ_iterator I,
DOTFuncMSSAInfo *CFGInfo) {
return "";
}
std::string getNodeAttributes(const BasicBlock *Node,
DOTFuncMSSAInfo *CFGInfo) {
return getNodeLabel(Node, CFGInfo).find(';') != std::string::npos
? "style=filled, fillcolor=lightpink"
: "";
}
};
}
bool MemorySSAPrinterLegacyPass::runOnFunction(Function &F) {
auto &MSSA = getAnalysis<MemorySSAWrapperPass>().getMSSA();
MSSA.ensureOptimizedUses();
if (DotCFGMSSA != "") {
DOTFuncMSSAInfo CFGInfo(F, MSSA);
WriteGraph(&CFGInfo, "", false, "MSSA", DotCFGMSSA);
} else
MSSA.print(dbgs());
if (VerifyMemorySSA)
MSSA.verifyMemorySSA();
return false;
}
AnalysisKey MemorySSAAnalysis::Key;
MemorySSAAnalysis::Result MemorySSAAnalysis::run(Function &F,
FunctionAnalysisManager &AM) {
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
auto &AA = AM.getResult<AAManager>(F);
return MemorySSAAnalysis::Result(std::make_unique<MemorySSA>(F, &AA, &DT));
}
bool MemorySSAAnalysis::Result::invalidate(
Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv) {
auto PAC = PA.getChecker<MemorySSAAnalysis>();
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()) ||
Inv.invalidate<AAManager>(F, PA) ||
Inv.invalidate<DominatorTreeAnalysis>(F, PA);
}
PreservedAnalyses MemorySSAPrinterPass::run(Function &F,
FunctionAnalysisManager &AM) {
auto &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA();
MSSA.ensureOptimizedUses();
if (DotCFGMSSA != "") {
DOTFuncMSSAInfo CFGInfo(F, MSSA);
WriteGraph(&CFGInfo, "", false, "MSSA", DotCFGMSSA);
} else {
OS << "MemorySSA for function: " << F.getName() << "\n";
MSSA.print(OS);
}
return PreservedAnalyses::all();
}
PreservedAnalyses MemorySSAWalkerPrinterPass::run(Function &F,
FunctionAnalysisManager &AM) {
auto &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA();
OS << "MemorySSA (walker) for function: " << F.getName() << "\n";
MemorySSAWalkerAnnotatedWriter Writer(&MSSA);
F.print(OS, &Writer);
return PreservedAnalyses::all();
}
PreservedAnalyses MemorySSAVerifierPass::run(Function &F,
FunctionAnalysisManager &AM) {
AM.getResult<MemorySSAAnalysis>(F).getMSSA().verifyMemorySSA();
return PreservedAnalyses::all();
}
char MemorySSAWrapperPass::ID = 0;
MemorySSAWrapperPass::MemorySSAWrapperPass() : FunctionPass(ID) {
initializeMemorySSAWrapperPassPass(*PassRegistry::getPassRegistry());
}
void MemorySSAWrapperPass::releaseMemory() { MSSA.reset(); }
void MemorySSAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequiredTransitive<DominatorTreeWrapperPass>();
AU.addRequiredTransitive<AAResultsWrapperPass>();
}
bool MemorySSAWrapperPass::runOnFunction(Function &F) {
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
MSSA.reset(new MemorySSA(F, &AA, &DT));
return false;
}
void MemorySSAWrapperPass::verifyAnalysis() const {
if (VerifyMemorySSA)
MSSA->verifyMemorySSA();
}
void MemorySSAWrapperPass::print(raw_ostream &OS, const Module *M) const {
MSSA->print(OS);
}
MemorySSAWalker::MemorySSAWalker(MemorySSA *M) : MSSA(M) {}
template <typename AliasAnalysisType>
MemoryAccess *
MemorySSA::ClobberWalkerBase<AliasAnalysisType>::getClobberingMemoryAccessBase(
MemoryAccess *StartingAccess, const MemoryLocation &Loc,
unsigned &UpwardWalkLimit) {
assert(!isa<MemoryUse>(StartingAccess) && "Use cannot be defining access");
Instruction *I = nullptr;
if (auto *StartingUseOrDef = dyn_cast<MemoryUseOrDef>(StartingAccess)) {
if (MSSA->isLiveOnEntryDef(StartingUseOrDef))
return StartingUseOrDef;
I = StartingUseOrDef->getMemoryInst();
if (!isa<CallBase>(I) && I->isFenceLike())
return StartingUseOrDef;
}
UpwardsMemoryQuery Q;
Q.OriginalAccess = StartingAccess;
Q.StartingLoc = Loc;
Q.Inst = nullptr;
Q.IsCall = false;
MemoryAccess *Clobber =
Walker.findClobber(StartingAccess, Q, UpwardWalkLimit);
LLVM_DEBUG({
dbgs() << "Clobber starting at access " << *StartingAccess << "\n";
if (I)
dbgs() << " for instruction " << *I << "\n";
dbgs() << " is " << *Clobber << "\n";
});
return Clobber;
}
static const Instruction *
getInvariantGroupClobberingInstruction(Instruction &I, DominatorTree &DT) {
if (!I.hasMetadata(LLVMContext::MD_invariant_group) || I.isVolatile())
return nullptr;
Value *PointerOperand = getLoadStorePointerOperand(&I)->stripPointerCasts();
if (isa<Constant>(PointerOperand))
return nullptr;
SmallVector<const Value *, 8> PointerUsesQueue;
PointerUsesQueue.push_back(PointerOperand);
const Instruction *MostDominatingInstruction = &I;
while (!PointerUsesQueue.empty()) {
const Value *Ptr = PointerUsesQueue.pop_back_val();
assert(Ptr && !isa<GlobalValue>(Ptr) &&
"Null or GlobalValue should not be inserted");
for (const User *Us : Ptr->users()) {
auto *U = dyn_cast<Instruction>(Us);
if (!U || U == &I || !DT.dominates(U, MostDominatingInstruction))
continue;
if (isa<BitCastInst>(U)) {
PointerUsesQueue.push_back(U);
continue;
}
if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) {
if (GEP->hasAllZeroIndices())
PointerUsesQueue.push_back(U);
continue;
}
if (U->hasMetadata(LLVMContext::MD_invariant_group) &&
getLoadStorePointerOperand(U) == Ptr && !U->isVolatile()) {
MostDominatingInstruction = U;
}
}
}
return MostDominatingInstruction == &I ? nullptr : MostDominatingInstruction;
}
template <typename AliasAnalysisType>
MemoryAccess *
MemorySSA::ClobberWalkerBase<AliasAnalysisType>::getClobberingMemoryAccessBase(
MemoryAccess *MA, unsigned &UpwardWalkLimit, bool SkipSelf,
bool UseInvariantGroup) {
auto *StartingAccess = dyn_cast<MemoryUseOrDef>(MA);
if (!StartingAccess)
return MA;
if (UseInvariantGroup) {
if (auto *I = getInvariantGroupClobberingInstruction(
*StartingAccess->getMemoryInst(), MSSA->getDomTree())) {
assert(isa<LoadInst>(I) || isa<StoreInst>(I));
auto *ClobberMA = MSSA->getMemoryAccess(I);
assert(ClobberMA);
if (isa<MemoryUse>(ClobberMA))
return ClobberMA->getDefiningAccess();
return ClobberMA;
}
}
bool IsOptimized = false;
if (StartingAccess->isOptimized()) {
if (!SkipSelf || !isa<MemoryDef>(StartingAccess))
return StartingAccess->getOptimized();
IsOptimized = true;
}
const Instruction *I = StartingAccess->getMemoryInst();
if (!isa<CallBase>(I) && I->isFenceLike())
return StartingAccess;
UpwardsMemoryQuery Q(I, StartingAccess);
if (isUseTriviallyOptimizableToLiveOnEntry(*Walker.getAA(), I)) {
MemoryAccess *LiveOnEntry = MSSA->getLiveOnEntryDef();
StartingAccess->setOptimized(LiveOnEntry);
StartingAccess->setOptimizedAccessType(None);
return LiveOnEntry;
}
MemoryAccess *OptimizedAccess;
if (!IsOptimized) {
MemoryAccess *DefiningAccess = StartingAccess->getDefiningAccess();
if (MSSA->isLiveOnEntryDef(DefiningAccess)) {
StartingAccess->setOptimized(DefiningAccess);
StartingAccess->setOptimizedAccessType(None);
return DefiningAccess;
}
OptimizedAccess = Walker.findClobber(DefiningAccess, Q, UpwardWalkLimit);
StartingAccess->setOptimized(OptimizedAccess);
if (MSSA->isLiveOnEntryDef(OptimizedAccess))
StartingAccess->setOptimizedAccessType(None);
else if (Q.AR && *Q.AR == AliasResult::MustAlias)
StartingAccess->setOptimizedAccessType(
AliasResult(AliasResult::MustAlias));
} else
OptimizedAccess = StartingAccess->getOptimized();
LLVM_DEBUG(dbgs() << "Starting Memory SSA clobber for " << *I << " is ");
LLVM_DEBUG(dbgs() << *StartingAccess << "\n");
LLVM_DEBUG(dbgs() << "Optimized Memory SSA clobber for " << *I << " is ");
LLVM_DEBUG(dbgs() << *OptimizedAccess << "\n");
MemoryAccess *Result;
if (SkipSelf && isa<MemoryPhi>(OptimizedAccess) &&
isa<MemoryDef>(StartingAccess) && UpwardWalkLimit) {
assert(isa<MemoryDef>(Q.OriginalAccess));
Q.SkipSelfAccess = true;
Result = Walker.findClobber(OptimizedAccess, Q, UpwardWalkLimit);
} else
Result = OptimizedAccess;
LLVM_DEBUG(dbgs() << "Result Memory SSA clobber [SkipSelf = " << SkipSelf);
LLVM_DEBUG(dbgs() << "] for " << *I << " is " << *Result << "\n");
return Result;
}
MemoryAccess *
DoNothingMemorySSAWalker::getClobberingMemoryAccess(MemoryAccess *MA) {
if (auto *Use = dyn_cast<MemoryUseOrDef>(MA))
return Use->getDefiningAccess();
return MA;
}
MemoryAccess *DoNothingMemorySSAWalker::getClobberingMemoryAccess(
MemoryAccess *StartingAccess, const MemoryLocation &) {
if (auto *Use = dyn_cast<MemoryUseOrDef>(StartingAccess))
return Use->getDefiningAccess();
return StartingAccess;
}
void MemoryPhi::deleteMe(DerivedUser *Self) {
delete static_cast<MemoryPhi *>(Self);
}
void MemoryDef::deleteMe(DerivedUser *Self) {
delete static_cast<MemoryDef *>(Self);
}
void MemoryUse::deleteMe(DerivedUser *Self) {
delete static_cast<MemoryUse *>(Self);
}
bool upward_defs_iterator::IsGuaranteedLoopInvariant(Value *Ptr) const {
auto IsGuaranteedLoopInvariantBase = [](Value *Ptr) {
Ptr = Ptr->stripPointerCasts();
if (!isa<Instruction>(Ptr))
return true;
return isa<AllocaInst>(Ptr);
};
Ptr = Ptr->stripPointerCasts();
if (auto *I = dyn_cast<Instruction>(Ptr)) {
if (I->getParent()->isEntryBlock())
return true;
}
if (auto *GEP = dyn_cast<GEPOperator>(Ptr)) {
return IsGuaranteedLoopInvariantBase(GEP->getPointerOperand()) &&
GEP->hasAllConstantIndices();
}
return IsGuaranteedLoopInvariantBase(Ptr);
}