| //===------ ForwardOpTree.h -------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Move instructions between statements. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "polly/ForwardOpTree.h" |
| |
| #include "polly/ScopBuilder.h" |
| #include "polly/ScopInfo.h" |
| #include "polly/ScopPass.h" |
| #include "polly/Support/GICHelper.h" |
| #include "polly/Support/VirtualInstruction.h" |
| #include "llvm/Analysis/ValueTracking.h" |
| |
| #define DEBUG_TYPE "polly-delicm" |
| |
| using namespace polly; |
| using namespace llvm; |
| |
| STATISTIC(TotalInstructionsCopied, "Number of copied instructions"); |
| STATISTIC(TotalReadOnlyCopied, "Number of copied read-only accesses"); |
| STATISTIC(TotalForwardedTrees, "Number of forwarded operand trees"); |
| STATISTIC(TotalModifiedStmts, |
| "Number of statements with at least one forwarded tree"); |
| |
| STATISTIC(ScopsModified, "Number of SCoPs with at least one forwarded tree"); |
| |
| namespace { |
| |
| /// The state of whether an operand tree was/can be forwarded. |
| enum ForwardingDecision { |
| FD_CannotForward, |
| FD_CanForward, |
| FD_CanForwardTree, |
| FD_DidForward, |
| }; |
| |
| /// Implementation of operand tree forwarding for a specific SCoP. |
| /// |
| /// For a statement that requires a scalar value (through a value read |
| /// MemoryAccess), see if its operand can be moved into the statement. If so, |
| /// the MemoryAccess is removed and the all the operand tree instructions are |
| /// moved into the statement. All original instructions are left in the source |
| /// statements. The simplification pass can clean these up. |
| class ForwardOpTreeImpl { |
| private: |
| /// The SCoP we are currently processing. |
| Scop *S; |
| |
| /// LoopInfo is required for VirtualUse. |
| LoopInfo *LI; |
| |
| /// How many instructions have been copied to other statements. |
| int NumInstructionsCopied = 0; |
| |
| /// How many read-only accesses have been copied. |
| int NumReadOnlyCopied = 0; |
| |
| /// How many operand trees have been forwarded. |
| int NumForwardedTrees = 0; |
| |
| /// Number of statements with at least one forwarded operand tree. |
| int NumModifiedStmts = 0; |
| |
| /// Whether we carried out at least one change to the SCoP. |
| bool Modified = false; |
| |
| void printStatistics(raw_ostream &OS, int Indent = 0) { |
| OS.indent(Indent) << "Statistics {\n"; |
| OS.indent(Indent + 4) << "Instructions copied: " << NumInstructionsCopied |
| << '\n'; |
| OS.indent(Indent + 4) << "Read-only accesses copied: " << NumReadOnlyCopied |
| << '\n'; |
| OS.indent(Indent + 4) << "Operand trees forwarded: " << NumForwardedTrees |
| << '\n'; |
| OS.indent(Indent + 4) << "Statements with forwarded operand trees: " |
| << NumModifiedStmts << '\n'; |
| OS.indent(Indent) << "}\n"; |
| } |
| |
| void printStatements(llvm::raw_ostream &OS, int Indent = 0) const { |
| OS.indent(Indent) << "After statements {\n"; |
| for (auto &Stmt : *S) { |
| OS.indent(Indent + 4) << Stmt.getBaseName() << "\n"; |
| for (auto *MA : Stmt) |
| MA->print(OS); |
| |
| OS.indent(Indent + 12); |
| Stmt.printInstructions(OS); |
| } |
| OS.indent(Indent) << "}\n"; |
| } |
| |
| /// Determines whether an operand tree can be forwarded or carries out a |
| /// forwarding, depending on the @p DoIt flag. |
| /// |
| /// @param TargetStmt The statement the operand tree will be copied to. |
| /// @param UseVal The value (usually an instruction) which is root of an |
| /// operand tree. |
| /// @param UseStmt The statement that uses @p UseVal. |
| /// @param UseLoop The loop @p UseVal is used in. |
| /// @param DoIt If false, only determine whether an operand tree can be |
| /// forwarded. If true, carry out the forwarding. Do not use |
| /// DoIt==true if an operand tree is not known to be |
| /// forwardable. |
| /// |
| /// @return If DoIt==false, return whether the operand tree can be forwarded. |
| /// If DoIt==true, return FD_DidForward. |
| ForwardingDecision canForwardTree(ScopStmt *TargetStmt, Value *UseVal, |
| ScopStmt *UseStmt, Loop *UseLoop, |
| bool DoIt) { |
| |
| // PHis are not yet supported. |
| if (isa<PHINode>(UseVal)) { |
| DEBUG(dbgs() << " Cannot forward PHI: " << *UseVal << "\n"); |
| return FD_CannotForward; |
| } |
| |
| VirtualUse VUse = VirtualUse::create(UseStmt, UseLoop, UseVal, true); |
| switch (VUse.getKind()) { |
| case VirtualUse::Constant: |
| case VirtualUse::Block: |
| case VirtualUse::Hoisted: |
| // These can be used anywhere without special considerations. |
| if (DoIt) |
| return FD_DidForward; |
| return FD_CanForward; |
| |
| case VirtualUse::Synthesizable: |
| // Not supported yet. |
| DEBUG(dbgs() << " Cannot forward synthesizable: " << *UseVal << "\n"); |
| return FD_CannotForward; |
| |
| case VirtualUse::ReadOnly: |
| if (!DoIt) |
| return FD_CanForward; |
| |
| // If we model read-only scalars, we need to create a MemoryAccess for it. |
| if (ModelReadOnlyScalars) |
| TargetStmt->ensureValueRead(UseVal); |
| |
| NumReadOnlyCopied++; |
| TotalReadOnlyCopied++; |
| return FD_DidForward; |
| |
| case VirtualUse::Intra: |
| case VirtualUse::Inter: |
| auto Inst = cast<Instruction>(UseVal); |
| |
| // Compatible instructions must satisfy the following conditions: |
| // 1. Idempotent (instruction will be copied, not moved; although its |
| // original instance might be removed by simplification) |
| // 2. Not access memory (There might be memory writes between) |
| // 3. Not cause undefined behaviour (we might copy to a location when the |
| // original instruction was no executed; this is currently not possible |
| // because we do not forward PHINodes) |
| // 4. Not leak memory if executed multiple times (I am looking at you, |
| // malloc!) |
| // |
| // Instruction::mayHaveSideEffects is not sufficient because it considers |
| // malloc to not have side-effects. llvm::isSafeToSpeculativelyExecute is |
| // not sufficient because it allows memory accesses. |
| if (mayBeMemoryDependent(*Inst)) { |
| DEBUG(dbgs() << " Cannot forward side-effect instruction: " << *Inst |
| << "\n"); |
| return FD_CannotForward; |
| } |
| |
| Loop *DefLoop = LI->getLoopFor(Inst->getParent()); |
| ScopStmt *DefStmt = S->getStmtFor(Inst); |
| assert(DefStmt && "Value must be defined somewhere"); |
| |
| if (DoIt) { |
| // To ensure the right order, prepend this instruction before its |
| // operands. This ensures that its operands are inserted before the |
| // instruction using them. |
| // TODO: The operand tree is not really a tree, but a DAG. We should be |
| // able to handle DAGs without duplication. |
| TargetStmt->prependInstruction(Inst); |
| NumInstructionsCopied++; |
| TotalInstructionsCopied++; |
| } |
| |
| for (Value *OpVal : Inst->operand_values()) { |
| ForwardingDecision OpDecision = |
| canForwardTree(TargetStmt, OpVal, DefStmt, DefLoop, DoIt); |
| switch (OpDecision) { |
| case FD_CannotForward: |
| assert(!DoIt); |
| return FD_CannotForward; |
| |
| case FD_CanForward: |
| case FD_CanForwardTree: |
| assert(!DoIt); |
| break; |
| |
| case FD_DidForward: |
| assert(DoIt); |
| break; |
| } |
| } |
| |
| if (DoIt) |
| return FD_DidForward; |
| return FD_CanForwardTree; |
| } |
| |
| llvm_unreachable("Case unhandled"); |
| } |
| |
| /// Try to forward an operand tree rooted in @p RA. |
| bool tryForwardTree(MemoryAccess *RA) { |
| assert(RA->isLatestScalarKind()); |
| DEBUG(dbgs() << "Trying to forward operand tree " << RA << "...\n"); |
| |
| ScopStmt *Stmt = RA->getStatement(); |
| Loop *InLoop = Stmt->getSurroundingLoop(); |
| |
| ForwardingDecision Assessment = |
| canForwardTree(Stmt, RA->getAccessValue(), Stmt, InLoop, false); |
| assert(Assessment != FD_DidForward); |
| if (Assessment != FD_CanForwardTree) |
| return false; |
| |
| ForwardingDecision Execution = |
| canForwardTree(Stmt, RA->getAccessValue(), Stmt, InLoop, true); |
| assert(Execution == FD_DidForward); |
| |
| Stmt->removeSingleMemoryAccess(RA); |
| return true; |
| } |
| |
| public: |
| ForwardOpTreeImpl(Scop *S, LoopInfo *LI) : S(S), LI(LI) {} |
| |
| /// Return which SCoP this instance is processing. |
| Scop *getScop() const { return S; } |
| |
| /// Run the algorithm: Use value read accesses as operand tree roots and try |
| /// to forward them into the statement. |
| bool forwardOperandTrees() { |
| for (ScopStmt &Stmt : *S) { |
| // Currently we cannot modify the instruction list of region statements. |
| if (!Stmt.isBlockStmt()) |
| continue; |
| |
| bool StmtModified = false; |
| |
| // Because we are modifying the MemoryAccess list, collect them first to |
| // avoid iterator invalidation. |
| SmallVector<MemoryAccess *, 16> Accs; |
| for (MemoryAccess *RA : Stmt) { |
| if (!RA->isRead()) |
| continue; |
| if (!RA->isLatestScalarKind()) |
| continue; |
| |
| Accs.push_back(RA); |
| } |
| |
| for (MemoryAccess *RA : Accs) { |
| if (tryForwardTree(RA)) { |
| Modified = true; |
| StmtModified = true; |
| NumForwardedTrees++; |
| TotalForwardedTrees++; |
| } |
| } |
| |
| if (StmtModified) { |
| NumModifiedStmts++; |
| TotalModifiedStmts++; |
| } |
| } |
| |
| if (Modified) |
| ScopsModified++; |
| return Modified; |
| } |
| |
| /// Print the pass result, performed transformations and the SCoP after the |
| /// transformation. |
| void print(llvm::raw_ostream &OS, int Indent = 0) { |
| printStatistics(OS, Indent); |
| |
| if (!Modified) { |
| // This line can easily be checked in regression tests. |
| OS << "ForwardOpTree executed, but did not modify anything\n"; |
| return; |
| } |
| |
| printStatements(OS, Indent); |
| } |
| }; |
| |
| /// Pass that redirects scalar reads to array elements that are known to contain |
| /// the same value. |
| /// |
| /// This reduces the number of scalar accesses and therefore potentially |
| /// increases the freedom of the scheduler. In the ideal case, all reads of a |
| /// scalar definition are redirected (We currently do not care about removing |
| /// the write in this case). This is also useful for the main DeLICM pass as |
| /// there are less scalars to be mapped. |
| class ForwardOpTree : public ScopPass { |
| private: |
| ForwardOpTree(const ForwardOpTree &) = delete; |
| const ForwardOpTree &operator=(const ForwardOpTree &) = delete; |
| |
| /// The pass implementation, also holding per-scop data. |
| std::unique_ptr<ForwardOpTreeImpl> Impl; |
| |
| public: |
| static char ID; |
| |
| explicit ForwardOpTree() : ScopPass(ID) {} |
| |
| virtual void getAnalysisUsage(AnalysisUsage &AU) const override { |
| AU.addRequiredTransitive<ScopInfoRegionPass>(); |
| AU.addRequired<LoopInfoWrapperPass>(); |
| AU.setPreservesAll(); |
| } |
| |
| virtual bool runOnScop(Scop &S) override { |
| // Free resources for previous SCoP's computation, if not yet done. |
| releaseMemory(); |
| |
| LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); |
| Impl = make_unique<ForwardOpTreeImpl>(&S, &LI); |
| |
| DEBUG(dbgs() << "Forwarding operand trees...\n"); |
| Impl->forwardOperandTrees(); |
| |
| DEBUG(dbgs() << "\nFinal Scop:\n"); |
| DEBUG(dbgs() << S); |
| |
| return false; |
| } |
| |
| virtual void printScop(raw_ostream &OS, Scop &S) const override { |
| if (!Impl) |
| return; |
| |
| assert(Impl->getScop() == &S); |
| Impl->print(OS); |
| } |
| |
| virtual void releaseMemory() override { Impl.reset(); } |
| |
| }; // class ForwardOpTree |
| |
| char ForwardOpTree::ID; |
| } // anonymous namespace |
| |
| ScopPass *polly::createForwardOpTreePass() { return new ForwardOpTree(); } |
| |
| INITIALIZE_PASS_BEGIN(ForwardOpTree, "polly-optree", |
| "Polly - Forward operand tree", false, false) |
| INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) |
| INITIALIZE_PASS_END(ForwardOpTree, "polly-optree", |
| "Polly - Forward operand tree", false, false) |