Files
clang-p2996/polly/lib/Support/VirtualInstruction.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

421 lines
14 KiB
C++

//===------ VirtualInstruction.cpp ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Tools for determining which instructions are within a statement and the
// nature of their operands.
//
//===----------------------------------------------------------------------===//
#include "polly/Support/VirtualInstruction.h"
#include "polly/Support/SCEVValidator.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);
// Uses by PHI nodes are always reading values written by other statements,
// except it is within a region statement.
if (PHINode *PHI = dyn_cast<PHINode>(UI)) {
// Handle PHI in exit block.
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);
// The MemoryAccess is expected to be set if @p Virtual is true.
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))
return VirtualUse(UserStmt, Val, Constant, nullptr, nullptr);
// Is the value synthesizable? If the user has been pruned
// (UserStmt == nullptr), it is either not used anywhere or is synthesizable.
// We assume synthesizable which practically should have the same effect.
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);
}
// FIXME: Inconsistency between lookupInvariantEquivClass and
// getRequiredInvariantLoads. Querying one of them should be enough.
auto &RIL = S->getRequiredInvariantLoads();
if (S->lookupInvariantEquivClass(Val) || RIL.count(dyn_cast<LoadInst>(Val)))
return VirtualUse(UserStmt, Val, Hoisted, nullptr, nullptr);
// ReadOnly uses may have MemoryAccesses that we want to associate with the
// use. This is why we look for a MemoryAccess here already.
MemoryAccess *InputMA = nullptr;
if (UserStmt && Virtual)
InputMA = UserStmt->lookupValueReadOf(Val);
// Uses are read-only if they have been defined before the SCoP, i.e., they
// cannot be written to inside the SCoP. Arguments are defined before any
// instructions, hence also before the SCoP. If the user has been pruned
// (UserStmt == nullptr) and is not SCEVable, assume it is read-only as it is
// neither an intra- nor an inter-use.
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);
// A use is inter-statement if either it is defined in another statement, or
// there is a MemoryAccess that reads its value that has been written by
// another statement.
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
/// Return true if @p Inst cannot be removed, even if it is nowhere referenced.
static bool isRoot(const Instruction *Inst) {
// The store is handled by its MemoryAccess. The load must be reached from the
// roots in order to be marked as used.
if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst))
return false;
// Terminator instructions (in region statements) are required for control
// flow.
if (Inst->isTerminator())
return true;
// Writes to memory must be honored.
if (Inst->mayWriteToMemory())
return true;
return false;
}
/// Return true for MemoryAccesses that cannot be removed because it represents
/// an llvm::Value that is used after the SCoP.
static bool isEscaping(MemoryAccess *MA) {
assert(MA->isOriginalValueKind());
Scop *S = MA->getStatement()->getParent();
return S->isEscaping(cast<Instruction>(MA->getAccessValue()));
}
/// Add non-removable virtual instructions in @p Stmt to @p RootInsts.
static void
addInstructionRoots(ScopStmt *Stmt,
SmallVectorImpl<VirtualInstruction> &RootInsts) {
if (!Stmt->isBlockStmt()) {
// In region statements the terminator statement and all statements that
// are not in the entry block cannot be eliminated and consequently must
// be roots.
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);
}
/// Add non-removable memory accesses in @p Stmt to @p RootInsts.
///
/// @param Local If true, all writes are assumed to escape. markAndSweep
/// algorithms can use this to be applicable to a single ScopStmt only without
/// the risk of removing definitions required by other statements.
/// If false, only writes for SCoP-escaping values are roots. This
/// is global mode, where such writes must be marked by theirs uses
/// in order to be reachable.
static void addAccessRoots(ScopStmt *Stmt,
SmallVectorImpl<MemoryAccess *> &RootAccs,
bool Local) {
for (auto *MA : *Stmt) {
if (!MA->isWrite())
continue;
// Writes to arrays are always used.
if (MA->isLatestArrayKind())
RootAccs.push_back(MA);
// Values are roots if they are escaping.
else if (MA->isLatestValueKind()) {
if (Local || isEscaping(MA))
RootAccs.push_back(MA);
}
// Exit phis are, by definition, escaping.
else if (MA->isLatestExitPHIKind())
RootAccs.push_back(MA);
// phi writes are only roots if we are not visiting the statement
// containing the PHINode.
else if (Local && MA->isLatestPHIKind())
RootAccs.push_back(MA);
}
}
/// Determine all instruction and access roots.
static void addRoots(ScopStmt *Stmt,
SmallVectorImpl<VirtualInstruction> &RootInsts,
SmallVectorImpl<MemoryAccess *> &RootAccs, bool Local) {
addInstructionRoots(Stmt, RootInsts);
addAccessRoots(Stmt, RootAccs, Local);
}
/// Mark accesses and instructions as used if they are reachable from a root,
/// walking the operand trees.
///
/// @param S The SCoP to walk.
/// @param LI The LoopInfo Analysis.
/// @param RootInsts List of root instructions.
/// @param RootAccs List of root accesses.
/// @param UsesInsts[out] Receives all reachable instructions, including the
/// roots.
/// @param UsedAccs[out] Receives all reachable accesses, including the roots.
/// @param OnlyLocal If non-nullptr, restricts walking to a single
/// statement.
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:
// Read-only scalars only have MemoryAccesses if ModelReadOnlyScalars is
// enabled.
if (!VUse.getMemoryAccess())
break;
LLVM_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) {
// We have two worklists to process: Only when the MemoryAccess worklist is
// empty, we process the instruction worklist.
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);
// Accesses to read-only values do not have a definition.
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 both worklists are empty, stop walking.
if (WorklistInsts.empty())
break;
VirtualInstruction VInst = WorklistInsts.pop_back_val();
ScopStmt *Stmt = VInst.getStmt();
Instruction *Inst = VInst.getInstruction();
// Do not process statements other than the local.
if (OnlyLocal && Stmt != OnlyLocal)
continue;
auto InsertResult = UsedInsts.insert(VInst);
if (!InsertResult.second)
continue;
// Add all operands to the worklists.
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);
}
// If there is an array access, also add its MemoryAccesses to the worklist.
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);
}