#include "polly/Support/VirtualInstruction.h"
using namespace polly;
using namespace llvm;
VirtualUse VirtualUse::create(Scop *S, const Use &U, LoopInfo *LI,
bool Virtual) {
auto *UserBB = getUseBlock(U);
Loop *UserScope = LI->getLoopFor(UserBB);
Instruction *UI = dyn_cast<Instruction>(U.getUser());
ScopStmt *UserStmt = S->getStmtFor(UI);
if (PHINode *PHI = dyn_cast<PHINode>(UI)) {
if (S->getRegion().getExit() == PHI->getParent())
return VirtualUse(UserStmt, U.get(), Inter, nullptr, nullptr);
if (UserStmt->getEntryBlock() != PHI->getParent())
return VirtualUse(UserStmt, U.get(), Intra, nullptr, nullptr);
MemoryAccess *IncomingMA = nullptr;
if (Virtual) {
if (const ScopArrayInfo *SAI =
S->getScopArrayInfoOrNull(PHI, MemoryKind::PHI)) {
IncomingMA = S->getPHIRead(SAI);
assert(IncomingMA->getStatement() == UserStmt);
}
}
return VirtualUse(UserStmt, U.get(), Inter, nullptr, IncomingMA);
}
return create(S, UserStmt, UserScope, U.get(), Virtual);
}
VirtualUse VirtualUse::create(Scop *S, ScopStmt *UserStmt, Loop *UserScope,
Value *Val, bool Virtual) {
assert(!isa<StoreInst>(Val) && "a StoreInst cannot be used");
if (isa<BasicBlock>(Val))
return VirtualUse(UserStmt, Val, Block, nullptr, nullptr);
if (isa<llvm::Constant>(Val) || isa<MetadataAsValue>(Val) ||
isa<InlineAsm>(Val))
return VirtualUse(UserStmt, Val, Constant, nullptr, nullptr);
auto *SE = S->getSE();
if (SE->isSCEVable(Val->getType())) {
auto *ScevExpr = SE->getSCEVAtScope(Val, UserScope);
if (!UserStmt || canSynthesize(Val, *UserStmt->getParent(), SE, UserScope))
return VirtualUse(UserStmt, Val, Synthesizable, ScevExpr, nullptr);
}
auto &RIL = S->getRequiredInvariantLoads();
if (S->lookupInvariantEquivClass(Val) || RIL.count(dyn_cast<LoadInst>(Val)))
return VirtualUse(UserStmt, Val, Hoisted, nullptr, nullptr);
MemoryAccess *InputMA = nullptr;
if (UserStmt && Virtual)
InputMA = UserStmt->lookupValueReadOf(Val);
if (!UserStmt || isa<Argument>(Val))
return VirtualUse(UserStmt, Val, ReadOnly, nullptr, InputMA);
auto Inst = cast<Instruction>(Val);
if (!S->contains(Inst))
return VirtualUse(UserStmt, Val, ReadOnly, nullptr, InputMA);
if (InputMA || (!Virtual && UserStmt != S->getStmtFor(Inst)))
return VirtualUse(UserStmt, Val, Inter, nullptr, InputMA);
return VirtualUse(UserStmt, Val, Intra, nullptr, nullptr);
}
void VirtualUse::print(raw_ostream &OS, bool Reproducible) const {
OS << "User: [" << User->getBaseName() << "] ";
switch (Kind) {
case VirtualUse::Constant:
OS << "Constant Op:";
break;
case VirtualUse::Block:
OS << "BasicBlock Op:";
break;
case VirtualUse::Synthesizable:
OS << "Synthesizable Op:";
break;
case VirtualUse::Hoisted:
OS << "Hoisted load Op:";
break;
case VirtualUse::ReadOnly:
OS << "Read-Only Op:";
break;
case VirtualUse::Intra:
OS << "Intra Op:";
break;
case VirtualUse::Inter:
OS << "Inter Op:";
break;
}
if (Val) {
OS << ' ';
if (Reproducible)
OS << '"' << Val->getName() << '"';
else
Val->print(OS, true);
}
if (ScevExpr) {
OS << ' ';
ScevExpr->print(OS);
}
if (InputMA && !Reproducible)
OS << ' ' << InputMA;
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void VirtualUse::dump() const {
print(errs(), false);
errs() << '\n';
}
#endif
void VirtualInstruction::print(raw_ostream &OS, bool Reproducible) const {
if (!Stmt || !Inst) {
OS << "[null VirtualInstruction]";
return;
}
OS << "[" << Stmt->getBaseName() << "]";
Inst->print(OS, !Reproducible);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void VirtualInstruction::dump() const {
print(errs(), false);
errs() << '\n';
}
#endif
static bool isRoot(const Instruction *Inst) {
if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst))
return false;
if (Inst->isTerminator())
return true;
if (Inst->mayWriteToMemory())
return true;
return false;
}
static bool isEscaping(MemoryAccess *MA) {
assert(MA->isOriginalValueKind());
Scop *S = MA->getStatement()->getParent();
return S->isEscaping(cast<Instruction>(MA->getAccessValue()));
}
static void
addInstructionRoots(ScopStmt *Stmt,
SmallVectorImpl<VirtualInstruction> &RootInsts) {
if (!Stmt->isBlockStmt()) {
RootInsts.emplace_back(Stmt,
Stmt->getRegion()->getEntry()->getTerminator());
for (BasicBlock *BB : Stmt->getRegion()->blocks())
if (Stmt->getRegion()->getEntry() != BB)
for (Instruction &Inst : *BB)
RootInsts.emplace_back(Stmt, &Inst);
return;
}
for (Instruction *Inst : Stmt->getInstructions())
if (isRoot(Inst))
RootInsts.emplace_back(Stmt, Inst);
}
static void addAccessRoots(ScopStmt *Stmt,
SmallVectorImpl<MemoryAccess *> &RootAccs,
bool Local) {
for (auto *MA : *Stmt) {
if (!MA->isWrite())
continue;
if (MA->isLatestArrayKind())
RootAccs.push_back(MA);
else if (MA->isLatestValueKind()) {
if (Local || isEscaping(MA))
RootAccs.push_back(MA);
}
else if (MA->isLatestExitPHIKind())
RootAccs.push_back(MA);
else if (Local && MA->isLatestPHIKind())
RootAccs.push_back(MA);
}
}
static void addRoots(ScopStmt *Stmt,
SmallVectorImpl<VirtualInstruction> &RootInsts,
SmallVectorImpl<MemoryAccess *> &RootAccs, bool Local) {
addInstructionRoots(Stmt, RootInsts);
addAccessRoots(Stmt, RootAccs, Local);
}
static void walkReachable(Scop *S, LoopInfo *LI,
ArrayRef<VirtualInstruction> RootInsts,
ArrayRef<MemoryAccess *> RootAccs,
DenseSet<VirtualInstruction> &UsedInsts,
DenseSet<MemoryAccess *> &UsedAccs,
ScopStmt *OnlyLocal = nullptr) {
UsedInsts.clear();
UsedAccs.clear();
SmallVector<VirtualInstruction, 32> WorklistInsts;
SmallVector<MemoryAccess *, 32> WorklistAccs;
WorklistInsts.append(RootInsts.begin(), RootInsts.end());
WorklistAccs.append(RootAccs.begin(), RootAccs.end());
auto AddToWorklist = [&](VirtualUse VUse) {
switch (VUse.getKind()) {
case VirtualUse::Block:
case VirtualUse::Constant:
case VirtualUse::Synthesizable:
case VirtualUse::Hoisted:
break;
case VirtualUse::ReadOnly:
if (!VUse.getMemoryAccess())
break;
[[fallthrough]];
case VirtualUse::Inter:
assert(VUse.getMemoryAccess());
WorklistAccs.push_back(VUse.getMemoryAccess());
break;
case VirtualUse::Intra:
WorklistInsts.emplace_back(VUse.getUser(),
cast<Instruction>(VUse.getValue()));
break;
}
};
while (true) {
while (!WorklistAccs.empty()) {
auto *Acc = WorklistAccs.pop_back_val();
ScopStmt *Stmt = Acc->getStatement();
if (OnlyLocal && Stmt != OnlyLocal)
continue;
auto Inserted = UsedAccs.insert(Acc);
if (!Inserted.second)
continue;
if (Acc->isRead()) {
const ScopArrayInfo *SAI = Acc->getScopArrayInfo();
if (Acc->isLatestValueKind()) {
MemoryAccess *DefAcc = S->getValueDef(SAI);
if (DefAcc)
WorklistAccs.push_back(S->getValueDef(SAI));
}
if (Acc->isLatestAnyPHIKind()) {
auto IncomingMAs = S->getPHIIncomings(SAI);
WorklistAccs.append(IncomingMAs.begin(), IncomingMAs.end());
}
}
if (Acc->isWrite()) {
if (Acc->isOriginalValueKind() ||
(Acc->isOriginalArrayKind() && Acc->getAccessValue())) {
Loop *Scope = Stmt->getSurroundingLoop();
VirtualUse VUse =
VirtualUse::create(S, Stmt, Scope, Acc->getAccessValue(), true);
AddToWorklist(VUse);
}
if (Acc->isOriginalAnyPHIKind()) {
for (auto Incoming : Acc->getIncoming()) {
VirtualUse VUse = VirtualUse::create(
S, Stmt, LI->getLoopFor(Incoming.first), Incoming.second, true);
AddToWorklist(VUse);
}
}
if (Acc->isOriginalArrayKind())
WorklistInsts.emplace_back(Stmt, Acc->getAccessInstruction());
}
}
if (WorklistInsts.empty())
break;
VirtualInstruction VInst = WorklistInsts.pop_back_val();
ScopStmt *Stmt = VInst.getStmt();
Instruction *Inst = VInst.getInstruction();
if (OnlyLocal && Stmt != OnlyLocal)
continue;
auto InsertResult = UsedInsts.insert(VInst);
if (!InsertResult.second)
continue;
PHINode *PHI = dyn_cast<PHINode>(Inst);
if (PHI && PHI->getParent() == Stmt->getEntryBlock()) {
if (MemoryAccess *PHIRead = Stmt->lookupPHIReadOf(PHI))
WorklistAccs.push_back(PHIRead);
} else {
for (VirtualUse VUse : VInst.operands())
AddToWorklist(VUse);
}
const MemoryAccessList *Accs = Stmt->lookupArrayAccessesFor(Inst);
if (!Accs)
continue;
for (MemoryAccess *Acc : *Accs)
WorklistAccs.push_back(Acc);
}
}
void polly::markReachable(Scop *S, LoopInfo *LI,
DenseSet<VirtualInstruction> &UsedInsts,
DenseSet<MemoryAccess *> &UsedAccs,
ScopStmt *OnlyLocal) {
SmallVector<VirtualInstruction, 32> RootInsts;
SmallVector<MemoryAccess *, 32> RootAccs;
if (OnlyLocal) {
addRoots(OnlyLocal, RootInsts, RootAccs, true);
} else {
for (auto &Stmt : *S)
addRoots(&Stmt, RootInsts, RootAccs, false);
}
walkReachable(S, LI, RootInsts, RootAccs, UsedInsts, UsedAccs, OnlyLocal);
}