//===------ 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(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(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(Val) && "a StoreInst cannot be used"); if (isa(Val)) return VirtualUse(UserStmt, Val, Block, nullptr, nullptr); if (isa(Val) || isa(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(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(Val)) return VirtualUse(UserStmt, Val, ReadOnly, nullptr, InputMA); auto Inst = cast(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(Inst) || isa(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(MA->getAccessValue())); } /// Add non-removable virtual instructions in @p Stmt to @p RootInsts. static void addInstructionRoots(ScopStmt *Stmt, SmallVectorImpl &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 &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 &RootInsts, SmallVectorImpl &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 RootInsts, ArrayRef RootAccs, DenseSet &UsedInsts, DenseSet &UsedAccs, ScopStmt *OnlyLocal = nullptr) { UsedInsts.clear(); UsedAccs.clear(); SmallVector WorklistInsts; SmallVector 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(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(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 &UsedInsts, DenseSet &UsedAccs, ScopStmt *OnlyLocal) { SmallVector RootInsts; SmallVector 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); }