| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // ReducibilityAnalysis.cpp // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "llvm/Analysis/ReducibilityAnalysis.h" |
| #include "dxc/Support/Global.h" |
| |
| #include "llvm/IR/CFG.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/LegacyPassManager.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <algorithm> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <vector> |
| |
| using namespace llvm; |
| using llvm::legacy::FunctionPassManager; |
| using llvm::legacy::PassManager; |
| using std::unordered_map; |
| using std::unordered_set; |
| using std::vector; |
| |
| #define DEBUG_TYPE "reducibility" |
| |
| //===----------------------------------------------------------------------===// |
| // Reducibility Analysis Pass |
| // |
| // The pass implements T1-T2 graph reducibility test. |
| // The algorithm can be found in "Engineering a Compiler" text by |
| // Keith Cooper and Linda Torczon. |
| // |
| //===----------------------------------------------------------------------===// |
| namespace ReducibilityAnalysisNS { |
| |
| class ReducibilityAnalysis : public FunctionPass { |
| public: |
| static char ID; |
| |
| ReducibilityAnalysis() |
| : FunctionPass(ID), m_Action(IrreducibilityAction::ThrowException), |
| m_bReducible(true) {} |
| |
| explicit ReducibilityAnalysis(IrreducibilityAction Action) |
| : FunctionPass(ID), m_Action(Action), m_bReducible(true) {} |
| |
| virtual bool runOnFunction(Function &F); |
| |
| virtual void getAnalysisUsage(AnalysisUsage &AU) const { |
| AU.setPreservesAll(); |
| } |
| |
| bool IsReducible() const { return m_bReducible; } |
| |
| private: |
| IrreducibilityAction m_Action; |
| bool m_bReducible; |
| }; |
| |
| char ReducibilityAnalysis::ID = 0; |
| |
| struct Node { |
| typedef unordered_set<unsigned> IdxSet; |
| IdxSet m_Succ; |
| IdxSet m_Pred; |
| }; |
| |
| class NodeWorklist { |
| public: |
| NodeWorklist(size_t MaxSize) : m_Size(0) { m_Data.resize(MaxSize); } |
| |
| size_t Size() const { return m_Size; } |
| unsigned Get(size_t idx) const { return m_Data[idx]; } |
| void PushBack(unsigned Val) { m_Data[m_Size++] = Val; } |
| void Clear() { m_Size = 0; } |
| |
| private: |
| size_t m_Size; |
| vector<unsigned> m_Data; |
| }; |
| |
| static bool IsEntryNode(size_t NodeIdx) { return NodeIdx == 0; } |
| |
| bool ReducibilityAnalysis::runOnFunction(Function &F) { |
| m_bReducible = true; |
| if (F.empty()) |
| return false; |
| IFTBOOL(F.size() < UINT32_MAX, DXC_E_DATA_TOO_LARGE); |
| |
| vector<Node> Nodes(F.size()); |
| unordered_map<BasicBlock *, unsigned> BasicBlockToNodeIdxMap; |
| |
| // |
| // Initialize. |
| // |
| unsigned iNode = 0; |
| for (BasicBlock &BB : F) { |
| BasicBlockToNodeIdxMap[&BB] = iNode++; |
| } |
| |
| for (BasicBlock &BB : F) { |
| BasicBlock *pBB = &BB; |
| unsigned N = BasicBlockToNodeIdxMap[pBB]; |
| |
| for (succ_iterator itSucc = succ_begin(pBB), endSucc = succ_end(pBB); |
| itSucc != endSucc; ++itSucc) { |
| BasicBlock *pSuccBB = *itSucc; |
| unsigned SuccNode = BasicBlockToNodeIdxMap[pSuccBB]; |
| Nodes[N].m_Succ.insert(SuccNode); |
| } |
| |
| for (pred_iterator itPred = pred_begin(pBB), endPred = pred_end(pBB); |
| itPred != endPred; ++itPred) { |
| BasicBlock *pPredBB = *itPred; |
| unsigned PredNode = BasicBlockToNodeIdxMap[pPredBB]; |
| Nodes[N].m_Pred.insert(PredNode); |
| } |
| } |
| |
| // |
| // Reduce. |
| // |
| NodeWorklist Q1(Nodes.size()), Q2(Nodes.size()); |
| NodeWorklist *pReady = &Q1, *pWaiting = &Q2; |
| for (unsigned i = 0; i < Nodes.size(); i++) { |
| pReady->PushBack(i); |
| } |
| |
| for (;;) { |
| bool bChanged = false; |
| pWaiting->Clear(); |
| |
| for (unsigned iNode = 0; iNode < pReady->Size(); iNode++) { |
| unsigned N = pReady->Get(iNode); |
| Node *pNode = &Nodes[N]; |
| |
| // T1: self-edge. |
| auto itSucc = pNode->m_Succ.find(N); |
| if (itSucc != pNode->m_Succ.end()) { |
| pWaiting->PushBack(N); |
| pNode->m_Succ.erase(itSucc); |
| auto s1 = pNode->m_Pred.erase(N); |
| DXASSERT_LOCALVAR(s1, s1 == 1, "otherwise check Pred/Succ sets"); |
| |
| bChanged = true; |
| continue; |
| } |
| |
| // T2: single predecessor. |
| if (pNode->m_Pred.size() == 1) { |
| unsigned PredNode = *pNode->m_Pred.begin(); |
| Node *pPredNode = &Nodes[PredNode]; |
| auto s1 = pPredNode->m_Succ.erase(N); |
| DXASSERT_LOCALVAR(s1, s1 == 1, "otherwise check Pred/Succ sets"); |
| // Do not update N's sets, as N is discarded and never looked at again. |
| |
| for (auto itSucc = pNode->m_Succ.begin(), endSucc = pNode->m_Succ.end(); |
| itSucc != endSucc; ++itSucc) { |
| unsigned SuccNode = *itSucc; |
| Node *pSuccNode = &Nodes[SuccNode]; |
| auto s2 = pSuccNode->m_Pred.erase(N); |
| DXASSERT_LOCALVAR(s2, s2, "otherwise check Pred/Succ sets"); |
| pPredNode->m_Succ.insert(SuccNode); |
| pSuccNode->m_Pred.insert(PredNode); |
| } |
| |
| bChanged = true; |
| continue; |
| } |
| |
| // Unreachable. |
| if (pNode->m_Pred.size() == 0 && !IsEntryNode(N)) { |
| for (auto itSucc = pNode->m_Succ.begin(), endSucc = pNode->m_Succ.end(); |
| itSucc != endSucc; ++itSucc) { |
| unsigned SuccNode = *itSucc; |
| Node *pSuccNode = &Nodes[SuccNode]; |
| auto s1 = pSuccNode->m_Pred.erase(N); |
| DXASSERT_LOCALVAR(s1, s1, "otherwise check Pred/Succ sets"); |
| } |
| |
| bChanged = true; |
| continue; |
| } |
| |
| // Could not reduce. |
| pWaiting->PushBack(N); |
| } |
| |
| if (pWaiting->Size() == 1) { |
| break; |
| } |
| |
| if (!bChanged) { |
| m_bReducible = false; |
| break; |
| } |
| |
| std::swap(pReady, pWaiting); |
| } |
| |
| if (!IsReducible()) { |
| switch (m_Action) { |
| case IrreducibilityAction::ThrowException: |
| DEBUG(dbgs() << "Function '" << F.getName() |
| << "' is irreducible. Aborting compilation.\n"); |
| IFT(DXC_E_IRREDUCIBLE_CFG); |
| break; |
| |
| case IrreducibilityAction::PrintLog: |
| DEBUG(dbgs() << "Function '" << F.getName() << "' is irreducible\n"); |
| break; |
| |
| case IrreducibilityAction::Ignore: |
| break; |
| |
| default: |
| DXASSERT(false, "otherwise incorrect action passed to the constructor"); |
| } |
| } |
| |
| return false; |
| } |
| |
| } // namespace ReducibilityAnalysisNS |
| |
| using namespace ReducibilityAnalysisNS; |
| |
| // Publicly exposed interface to pass... |
| char &llvm::ReducibilityAnalysisID = ReducibilityAnalysis::ID; |
| |
| INITIALIZE_PASS_BEGIN(ReducibilityAnalysis, "red", "Reducibility Analysis", |
| true, true) |
| INITIALIZE_PASS_END(ReducibilityAnalysis, "red", "Reducibility Analysis", true, |
| true) |
| |
| namespace llvm { |
| |
| FunctionPass *createReducibilityAnalysisPass(IrreducibilityAction Action) { |
| return new ReducibilityAnalysis(Action); |
| } |
| |
| bool IsReducible(const Module &M, IrreducibilityAction Action) { |
| PassManager PM; |
| ReducibilityAnalysis *pRA = new ReducibilityAnalysis(Action); |
| PM.add(pRA); |
| PM.run(const_cast<Module &>(M)); |
| |
| return pRA->IsReducible(); |
| } |
| |
| bool IsReducible(const Function &f, IrreducibilityAction Action) { |
| Function &F = const_cast<Function &>(f); |
| DXASSERT(!F.isDeclaration(), |
| "otherwise the caller is asking to check an external function"); |
| |
| FunctionPassManager FPM(F.getParent()); |
| ReducibilityAnalysis *pRA = new ReducibilityAnalysis(Action); |
| FPM.add(pRA); |
| FPM.doInitialization(); |
| FPM.run(F); |
| |
| return pRA->IsReducible(); |
| } |
| |
| } // namespace llvm |