/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

//
// This file implements FunctionGroup, FunctionGroupAnalysis and
// FunctionGroupPass. See FunctionGroup.h for more details.
//
// The FunctionGroupPass part was adapted from CallGraphSCCPass.cpp.
//
// This file is currently in lib/Target/GenX, as that is the only place it
// is used. It could be moved somewhere more general.
//
//===----------------------------------------------------------------------===//

#include "FunctionGroup.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/GenXIntrinsics/GenXMetadata.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
using namespace llvm;

#include "llvmWrapper/IR/LegacyPassManagers.h"
#include "llvmWrapper/IR/PassTimingInfo.h"
#include "Probe/Assertion.h"


#define DEBUG_TYPE "functiongroup-passmgr"

/***********************************************************************
 * FunctionGroupAnalysis implementation
 */
char FunctionGroupAnalysis::ID = 0;
INITIALIZE_PASS(FunctionGroupAnalysis, "FunctionGroupAnalysis",
                "FunctionGroupAnalysis", false, true /*analysis*/)

ModulePass *llvm::createFunctionGroupAnalysisPass() {
  initializeFunctionGroupAnalysisPass(*PassRegistry::getPassRegistry());
  return new FunctionGroupAnalysis();
}

// clear : clear out the analysis
void FunctionGroupAnalysis::clear() {
  for (auto T : TypesToProcess)
    GroupMap[T].clear();

  for (auto i = begin(), e = end(); i != e; ++i)
    delete *i;
  for (auto i = NonMainGroups.begin(), e = NonMainGroups.end(); i != e; ++i)
    delete *i;

  Groups.clear();
  NonMainGroups.clear();
  M = nullptr;
}

FunctionGroup *FunctionGroupAnalysis::getGroup(const Function *F,
                                               FGType Type) const {
  auto i = GroupMap[Type].find(F);
  if (i == GroupMap[Type].end())
    return nullptr;
  return i->second;
}

// getGroup : get the FunctionGroup containing Function F, else 0
FunctionGroup *FunctionGroupAnalysis::getGroup(const Function *F) const {
  return getGroup(F, FGType::GROUP);
}

FunctionGroup *FunctionGroupAnalysis::getSubGroup(const Function *F) const {
  return getGroup(F, FGType::SUBGROUP);
}

// getGroupForHead : get the FunctionGroup for which Function F is the
// head, else 0
FunctionGroup *FunctionGroupAnalysis::getGroupForHead(const Function *F) const {
  auto *FG = getGroup(F);
  IGC_ASSERT(FG->size());
  if (*FG->begin() == F)
    return FG;
  return nullptr;
}

// replaceFunction : replace a Function in a FunctionGroup
// An in-use iterator in the modified FunctionGroup remains valid.
void FunctionGroupAnalysis::replaceFunction(Function *OldF, Function *NewF) {
  for (auto T : TypesToProcess) {
    auto OldFIt = GroupMap[T].find(OldF);
    IGC_ASSERT(OldFIt != GroupMap[T].end());
    FunctionGroup *FG = OldFIt->second;
    GroupMap[T].erase(OldFIt);
    GroupMap[T][NewF] = FG;
    for (auto i = FG->begin();; ++i) {
      IGC_ASSERT(i != FG->end());
      if (*i == OldF) {
        *i = NewF;
        break;
      }
    }
  }
}

// addToFunctionGroup : add Function F to FunctionGroup FG
// Using this (rather than calling push_back directly on the FunctionGroup)
// means that the mapping from F to FG will be created, and getGroup() will
// work for this Function.
void FunctionGroupAnalysis::addToFunctionGroup(FunctionGroup *FG, Function *F,
                                               FGType Type) {
  IGC_ASSERT(FG);
  IGC_ASSERT_MESSAGE(FG->getParent()->getModule() == M,
    "attaching to FunctionGroup from wrong Module");
  IGC_ASSERT_MESSAGE(!GroupMap[Type][F],
    "Function already attached to FunctionGroup");
  GroupMap[Type][F] = FG;
  FG->push_back(F);
}

// createFunctionGroup : create new FunctionGroup for which F is the head
FunctionGroup *FunctionGroupAnalysis::createFunctionGroup(Function *F,
                                                          FGType Type) {
  auto FG = new FunctionGroup(this);
  if (Type == FGType::GROUP)
    Groups.push_back(FG);
  else
    NonMainGroups.push_back(FG);
  addToFunctionGroup(FG, F, Type);
  return FG;
}

// Returns true if pass is simple module pass,
// e.g. it is neither FG pass nor function pass manager.
static bool isModulePass(Pass *P) {
  if (P->getPassKind() != PT_Module)
    return false;
  return !P->getAsPMDataManager();
}

static StringRef TypeToAttr(FunctionGroupAnalysis::FGType Type) {
  switch (Type) {
  case FunctionGroupAnalysis::FGType::GROUP:
    return genx::FunctionMD::CMGenXMain;
  case FunctionGroupAnalysis::FGType::SUBGROUP:
    return genx::FunctionMD::CMStackCall;
  default:
    IGC_ASSERT_EXIT_MESSAGE(0, "Can't gen attribute for nox-existent FG type");
    break;
  }
  return "";
}

bool FunctionGroupAnalysis::buildGroup(CallGraph &Callees, Function *F,
                                       FunctionGroup *curGr, FGType Type) {
  bool result = false;
  LLVM_DEBUG(dbgs() << "process function " << F->getName() << " from " << curGr
                    << ", type = " << Type << "\n");
  if (Visited.count(F) > 0) {
    if (GroupMap[Type].count(F) > 0 && GroupMap[Type][F] != curGr &&
        !F->hasFnAttribute(TypeToAttr(Type))) {
      ValueToValueMapTy VMap;
      Function *ClonedFunc = CloneFunction(F, VMap);
      LLVM_DEBUG(dbgs() << "Cloning: " << ClonedFunc->getName() << "\n");

      result = true;

      for (auto it = F->use_begin(); it != F->use_end();) {
        Use *u = &*it++;
        auto *CI = dyn_cast<CallInst>(u->getUser());
        IGC_ASSERT(CI);
        if (GroupMap[Type][CI->getFunction()] == curGr)
          *u = ClonedFunc;
      }
      for (auto T : TypesToProcess) {
        if (T >= Type)
          break;
        addToFunctionGroup(getGroup(F, T), ClonedFunc, T);
      }
      addToFunctionGroup(curGr, ClonedFunc, Type);

      for (auto &Callee : Callees[F]) {
        if (Callee == F)
          continue;
        LLVM_DEBUG(dbgs() << "Next callee: " << Callee->getName() << "\n");
        result |= buildGroup(Callees, Callee, curGr, Type);
      }
    }
  } else if (!Visited.count(F)) {
    Visited[F] = true;
    // group is created either on a function with a corresponding attribute
    // or on a root of a whole function tree that is kernel (genx_main)
    if (F->hasFnAttribute(TypeToAttr(Type)) ||
        F->hasFnAttribute(genx::FunctionMD::CMGenXMain)) {
      LLVM_DEBUG(dbgs() << "Create new group of type " << Type << "\n");
      curGr = createFunctionGroup(F, Type);
    } else if (curGr) {
      LLVM_DEBUG(dbgs() << "Add to group " << curGr->getHead()->getName()
                        << " of type " << Type << "\n");
      addToFunctionGroup(curGr, F, Type);
    }
    for (auto &Callee : Callees[F]) {
      LLVM_DEBUG(dbgs() << "Next callee: " << Callee->getName() << "\n");
      result |= buildGroup(Callees, Callee, curGr, Type);
    }
  }
  LLVM_DEBUG(dbgs() << "finish processing function " << F->getName()
                    << " on level " << Type << "\n");
  return result;
}

//===----------------------------------------------------------------------===//
// FGPassManager
//
/// FGPassManager manages FPPassManagers and FunctionGroupPasses.
/// It actually now imitates MPPassManager because there is no way
/// to extend pass manager structure without modification of
/// LLVM pass managers code.
/// This pass is injected into pass manager stack instead of top-level
/// MPPassManager when there is first time FunctionGroupPass is created.
/// After this manager replaces MPPassManager, it handles all Module and
/// FunctionGroup passes. This manager itself is module pass so it is
/// actually contained in list of module passes of module pass manager
/// as last pass that should be run. However, top-level pass manager do
/// not know anything about this FGPassManager except that it is indirect
/// pass manager, so it will not run it directly.

namespace {

class FGPassManager : public ModulePass, public IGCLLVM::PMDataManager {
public:
  static char ID;
  explicit FGPassManager() : ModulePass(ID), IGCLLVM::PMDataManager() {}

  /// run - Execute all of the passes scheduled for execution.  Keep track of
  /// whether any of the passes modifies the module, and if so, return true.
  bool runOnModule(Module &M) override;

  bool doInitialization(Module &M) override;
  bool doFinalization(Module &M) override;

  /// Pass Manager itself does not invalidate any analysis info.
  void getAnalysisUsage(AnalysisUsage &Info) const override {
    // FGPassManager needs FunctionGroupAnalysis.
    Info.addRequired<FunctionGroupAnalysis>();
    Info.setPreservesAll();
  }

  StringRef getPassName() const override {
    return "FunctionGroup Pass Manager";
  }

  PMDataManager *getAsPMDataManager() override { return this; }
  Pass *getAsPass() override { return this; }

  // Print passes managed by this manager
  void dumpPassStructure(unsigned Offset) override {
    errs().indent(Offset * 2) << "FunctionGroup Pass Manager\n";
    for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) {
      Pass *P = getContainedPass(Index);
      unsigned DumpOffset = Offset + 1;
      // Pretend that there is no FGPassManager when we need to dump
      // module pass indentation.
      if (isModulePass(P))
        DumpOffset -= 1;
      P->dumpPassStructure(DumpOffset);
      dumpLastUses(P, DumpOffset);
    }
  }

  Pass *getContainedPass(unsigned N) {
    IGC_ASSERT_MESSAGE(N < PassVector.size(), "Pass number out of range!");
    return static_cast<Pass *>(PassVector[N]);
  }

  PassManagerType getPassManagerType() const override {
    return PMT_ModulePassManager;
  }

private:
  bool runPassesOnFunctionGroup(unsigned Begin, unsigned End, FunctionGroup &FG);
  bool runPassOnFunctionGroup(Pass *P, FunctionGroup &FG);
  bool doFGInitialization(unsigned Begin, unsigned End, FunctionGroupAnalysis &FGA);
  bool doFGFinalization(unsigned Begin, unsigned End, FunctionGroupAnalysis &FGA);
  bool runFGPassSequence(unsigned &Pass);
  bool runModulePassSequence(unsigned &Pass, Module &M);
};

} // end anonymous namespace.

char FGPassManager::ID = 0;

bool FGPassManager::runPassOnFunctionGroup(Pass *P, FunctionGroup &FG) {
  bool Changed = false;
  llvm::PMDataManager *PM = P->getAsPMDataManager();

  if (!PM) {
    FunctionGroupPass *CGSP = (FunctionGroupPass *)P;
    {
      TimeRegion PassTimer(getPassTimer(CGSP));
      Changed = CGSP->runOnFunctionGroup(FG);
    }
    return Changed;
  }

  // TODO: there may be also SCC pass manager.
  IGC_ASSERT_MESSAGE(PM->getPassManagerType() == PMT_FunctionPassManager,
    "Invalid FGPassManager member");
  FPPassManager *FPP = (FPPassManager *)P;

  // Run pass P on all functions in the current FunctionGroup.
  for (auto &F : FG) {
    dumpPassInfo(P, EXECUTION_MSG, ON_FUNCTION_MSG, F->getName());
    {
      TimeRegion PassTimer(getPassTimer(FPP));
      Changed |= FPP->runOnFunction(*F);
    }
    F->getContext().yield();
  }
  return Changed;
}


/// RunPassesOnFunctionGroup -  Execute sequential passes of pass manager
/// on the specified FunctionGroup
bool FGPassManager::runPassesOnFunctionGroup(unsigned Begin, unsigned End,
                                             FunctionGroup &FG) {
  bool Changed = false;

  // Run selected passes on current FunctionGroup.
  for (unsigned PassNo = Begin; PassNo != End; ++PassNo) {
    Pass *P = getContainedPass(PassNo);
    dumpRequiredSet(P);

    initializeAnalysisImpl(P);

    // Actually run this pass on the current FunctionGroup.
    Changed |= runPassOnFunctionGroup(P, FG);
    if (Changed)
      dumpPassInfo(P, MODIFICATION_MSG, ON_MODULE_MSG, "");
    dumpPreservedSet(P);

    verifyPreservedAnalysis(P);
    removeNotPreservedAnalysis(P);
    recordAvailableAnalysis(P);
    removeDeadPasses(P, "", ON_MODULE_MSG);
  }

  return Changed;
}

/// Initialize sequential FG passes
bool FGPassManager::doFGInitialization(unsigned Begin, unsigned End,
                                       FunctionGroupAnalysis &FGA) {
  bool Changed = false;

  for (unsigned i = Begin; i != End; ++i) {
    if (llvm::PMDataManager *PM = getContainedPass(i)->getAsPMDataManager()) {
      // TODO: SCC PassManager?
      IGC_ASSERT_MESSAGE(PM->getPassManagerType() == PMT_FunctionPassManager,
        "Invalid FGPassManager member");
      Changed |= ((FPPassManager*)PM)->doInitialization(*FGA.getModule());
    } else {
      Changed |=
          ((FunctionGroupPass *)getContainedPass(i))->doInitialization(FGA);
    }
  }

  return Changed;
}

/// Finalize sequential FG passes
bool FGPassManager::doFGFinalization(unsigned Begin, unsigned End,
                                     FunctionGroupAnalysis &FGA) {
  bool Changed = false;

  for (int i = End - 1; i >= static_cast<int>(Begin); --i) {
    if (llvm::PMDataManager *PM = getContainedPass(i)->getAsPMDataManager()) {
      // TODO: SCC PassManager?
      IGC_ASSERT_MESSAGE(PM->getPassManagerType() == PMT_FunctionPassManager,
        "Invalid FGPassManager member");
      Changed |= ((FPPassManager*)PM)->doFinalization(*FGA.getModule());
    } else {
      Changed |=
          ((FunctionGroupPass *)getContainedPass(i))->doFinalization(FGA);
    }
  }

  return Changed;
}

bool FGPassManager::runFGPassSequence(unsigned &Pass) {
  const unsigned BeginPass = Pass;
  const unsigned NumPasses = getNumContainedPasses();
  while (Pass < NumPasses && !isModulePass(getContainedPass(Pass)))
    ++Pass;

  // Function group analysis may be invalidated by previous
  // module passes so we will need to query it every time we
  // execute sequence of passes.
  FunctionGroupAnalysis &FGA = getAnalysis<FunctionGroupAnalysis>();
  bool Changed = false;

  Changed |= doFGInitialization(BeginPass, Pass, FGA);
  for (auto *FG : FGA)
    Changed |= runPassesOnFunctionGroup(BeginPass, Pass, *FG);
  Changed |= doFGFinalization(BeginPass, Pass, FGA);

  return Changed;
}

bool FGPassManager::runModulePassSequence(unsigned &Pass, Module &M) {
  const unsigned BeginPass = Pass;
  const unsigned NumPasses = getNumContainedPasses();
  while (Pass < NumPasses && isModulePass(getContainedPass(Pass)))
    ++Pass;

  bool Changed = false;

  // Copied from MPPassManager in LegacyPassManager.cpp.
  unsigned InstrCount, ModuleCount = 0;
  StringMap<std::pair<unsigned, unsigned>> FunctionToInstrCount;
  bool EmitICRemark = M.shouldEmitInstrCountChangedRemark();
  // Collect the initial size of the module.
  if (EmitICRemark) {
    InstrCount = initSizeRemarkInfo(M, FunctionToInstrCount);
    ModuleCount = InstrCount;
  }

  for (unsigned Index = BeginPass; Index < Pass; ++Index) {
    auto *MP = static_cast<ModulePass *>(getContainedPass(Index));
    bool LocalChanged = false;

    dumpPassInfo(MP, EXECUTION_MSG, ON_MODULE_MSG, M.getModuleIdentifier());
    dumpRequiredSet(MP);

    initializeAnalysisImpl(MP);

    {
      PassManagerPrettyStackEntry X(MP, M);
      TimeRegion PassTimer(getPassTimer(MP));

      LocalChanged |= MP->runOnModule(M);
      if (EmitICRemark) {
        // Update the size of the module.
        ModuleCount = M.getInstructionCount();
        if (ModuleCount != InstrCount) {
          int64_t Delta = static_cast<int64_t>(ModuleCount) -
            static_cast<int64_t>(InstrCount);
          emitInstrCountChangedRemark(MP, M, Delta, InstrCount,
            FunctionToInstrCount);
          InstrCount = ModuleCount;
        }
      }
    }

    Changed |= LocalChanged;
    if (LocalChanged)
      dumpPassInfo(MP, MODIFICATION_MSG, ON_MODULE_MSG,
        M.getModuleIdentifier());
    dumpPreservedSet(MP);
    dumpUsedSet(MP);

    verifyPreservedAnalysis(MP);
    removeNotPreservedAnalysis(MP);
    recordAvailableAnalysis(MP);
    removeDeadPasses(MP, M.getModuleIdentifier(), ON_MODULE_MSG);
  }

  return Changed;
}

/// run - Execute all of the passes scheduled for execution.  Keep track of
/// whether any of the passes modifies the module, and if so, return true.
bool FGPassManager::runOnModule(Module &M) {
  bool Changed = false;

  unsigned CurPass = 0;
  unsigned NumPasses = getNumContainedPasses();
  while (CurPass != NumPasses) {
    // We will always have chain of fg passes followed by
    // module passes repeating until there are no passes.
    Changed |= runFGPassSequence(CurPass);
    Changed |= runModulePassSequence(CurPass, M);
  }

  return Changed;
}

bool FGPassManager::doInitialization(Module &M) {
  bool Changed = false;

  // Initialize module passes
  for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) {
    auto *P = getContainedPass(Index);
    if (isModulePass(P))
      Changed |= P->doInitialization(M);
  }

  return Changed;
}

bool FGPassManager::doFinalization(Module &M) {
  bool Changed = false;

  // Finalize module passes
  for (int Index = getNumContainedPasses() - 1; Index >= 0; --Index) {
    auto *P = getContainedPass(Index);
    if (isModulePass(P))
      Changed |= P->doFinalization(M);
  }

  return Changed;
}

//===----------------------------------------------------------------------===//
// FunctionGroupPass Implementation
//===----------------------------------------------------------------------===//

/// Assign pass manager to manage this pass.
void FunctionGroupPass::assignPassManager(PMStack &PMS,
                                          PassManagerType PreferredType) {
  // Find module pass manager.
  while (!PMS.empty() &&
         PMS.top()->getPassManagerType() > PMT_ModulePassManager)
    PMS.pop();

  IGC_ASSERT_MESSAGE(!PMS.empty(), "Unable to handle FunctionGroup Pass");
  FGPassManager *GFP;

  // Check whether this ModulePassManager is our injected function
  // group pass manager. If not, replace old module pass manager
  // with one for function groups.
  auto *PM = PMS.top();
  IGC_ASSERT_MESSAGE(PM->getPassManagerType() == PMT_ModulePassManager,
    "Bad pass manager type for function group pass manager");
  if (PM->getAsPass()->getPassID() == &FGPassManager::ID)
    GFP = static_cast<FGPassManager *>(PM);
  else {
    // Create new FunctionGroup Pass Manager if it does not exist.

    // [1] Create new FunctionGroup Pass Manager
    GFP = new FGPassManager();

    // [2] Set up new manager's top level manager
    PMTopLevelManager *TPM = PM->getTopLevelManager();
    TPM->addIndirectPassManager(GFP);
    GFP->setTopLevelManager(TPM);

    // [3] Assign manager to manage this new manager. This should not create
    // and push new managers into PMS
    TPM->schedulePass(GFP);
    IGC_ASSERT_MESSAGE(PMS.top() == PM, "Pass manager unexpectedly changed");

    // [4] Steal analysis info from module pass manager.
    *GFP->getAvailableAnalysis() = std::move(*PM->getAvailableAnalysis());

    // [5] Replace module pass manager with function group pass manager.
    PMS.pop();
    PMS.push(GFP);
  }

  GFP->add(this);
}

/// getAnalysisUsage - For this class, we declare that we require and preserve
/// FunctionGroupAnalysis.  If the derived class implements this method, it
/// should always explicitly call the implementation here.
void FunctionGroupPass::getAnalysisUsage(AnalysisUsage &AU) const {
  AU.addRequired<FunctionGroupAnalysis>();
  AU.addPreserved<FunctionGroupAnalysis>();
}

//===----------------------------------------------------------------------===//
// PrintFunctionGroupPass Implementation
//===----------------------------------------------------------------------===//

namespace {
/// PrintFunctionGroupPass - Print a FunctionGroup
///
class PrintFunctionGroupPass : public FunctionGroupPass {
  std::string Banner;
  raw_ostream &Out; // raw_ostream to print on.
public:
  static char ID;
  PrintFunctionGroupPass(const std::string &B, raw_ostream &o)
      : FunctionGroupPass(ID), Banner(B), Out(o) {}

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.setPreservesAll();
  }

  bool runOnFunctionGroup(FunctionGroup &FG) override {
    Out << Banner;
    for (auto I = FG.begin(), E = FG.end(); I != E; ++I) {
      Function *F = *I;
      Out << Banner << static_cast<Value &>(*F);
    }
    return false;
  }
};
} // end anonymous namespace.

char PrintFunctionGroupPass::ID = 0;

Pass *FunctionGroupPass::createPrinterPass(raw_ostream &O,
                                           const std::string &Banner) const {
  return new PrintFunctionGroupPass(Banner, O);
}

//===----------------------------------------------------------------------===//
//  DominatorTreeGroupWrapperPass Implementation
//===----------------------------------------------------------------------===//
//
// The implementation details of the wrapper pass that holds a DominatorTree
// per Function in a FunctionGroup.
//
//===----------------------------------------------------------------------===//
char DominatorTreeGroupWrapperPass::ID = 0;
INITIALIZE_PASS_BEGIN(DominatorTreeGroupWrapperPass, "groupdomtree",
                      "Group Dominator Tree Construction", true, true)
INITIALIZE_PASS_END(DominatorTreeGroupWrapperPass, "groupdomtree",
                    "Group Dominator Tree Construction", true, true)

void DominatorTreeGroupWrapperPass::releaseMemory() {
  for (auto i = DTs.begin(), e = DTs.end(); i != e; ++i)
    delete i->second;
  DTs.clear();
}

bool DominatorTreeGroupWrapperPass::runOnFunctionGroup(FunctionGroup &FG) {
  for (auto fgi = FG.begin(), fge = FG.end(); fgi != fge; ++fgi) {
    Function *F = *fgi;
    auto DT = new DominatorTree;
    DT->recalculate(*F);
    DTs[F] = DT;
  }
  return false;
}

void DominatorTreeGroupWrapperPass::verifyAnalysis() const {
  for (auto i = DTs.begin(), e = DTs.end(); i != e; ++i)
    i->second->verify();
}

void DominatorTreeGroupWrapperPass::print(raw_ostream &OS,
                                          const Module *) const {
  for (auto i = DTs.begin(), e = DTs.end(); i != e; ++i)
    i->second->print(OS);
}

//===----------------------------------------------------------------------===//
//  LoopInfoGroupWrapperPass Implementation
//===----------------------------------------------------------------------===//
//
// The implementation details of the wrapper pass that holds a LoopInfo
// per Function in a FunctionGroup.
//
//===----------------------------------------------------------------------===//
char LoopInfoGroupWrapperPass::ID = 0;
INITIALIZE_PASS_BEGIN(LoopInfoGroupWrapperPass, "grouploopinfo",
                      "Group Loop Info Construction", true, true)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeGroupWrapperPass)
INITIALIZE_PASS_END(LoopInfoGroupWrapperPass, "grouploopinfo",
                    "Group Loop Info Construction", true, true)

void LoopInfoGroupWrapperPass::releaseMemory() {
  for (auto i = LIs.begin(), e = LIs.end(); i != e; ++i)
    delete i->second;
  LIs.clear();
}

bool LoopInfoGroupWrapperPass::runOnFunctionGroup(FunctionGroup &FG) {
  auto &DTs = getAnalysis<DominatorTreeGroupWrapperPass>();
  for (auto fgi = FG.begin(), fge = FG.end(); fgi != fge; ++fgi) {
    Function *F = *fgi;
    auto LI = new LoopInfo;
    LI->analyze(*DTs.getDomTree(F));
    LIs[F] = LI;
  }
  return false;
}

void LoopInfoGroupWrapperPass::verifyAnalysis() const {
  auto &DTs = getAnalysis<DominatorTreeGroupWrapperPass>();
  for (auto i = LIs.begin(), e = LIs.end(); i != e; ++i)
    i->second->verify(*DTs.getDomTree(i->first));
}

void LoopInfoGroupWrapperPass::print(raw_ostream &OS, const Module *) const {
  for (auto i = LIs.begin(), e = LIs.end(); i != e; ++i)
    i->second->print(OS);
}
