#ifndef _HI_FunctionInstantiation
#define _HI_FunctionInstantiation
// related headers should be included.
#include "HI_StringProcess.h"
#include "HI_print.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include <ios>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/time.h>
#include <vector>

using namespace llvm;

class HI_FunctionInstantiation : public ModulePass
{
  public:

    ~HI_FunctionInstantiation()
    {
        FuncInitiationLog->flush();
        delete FuncInitiationLog;
    }

    std::string topFunctionName;

    std::vector<Instruction *> CallInst_List;
    std::vector<Function *> FunctionShouldBeInitiated;
    std::map<Function *, int> IntitiatedFunctionCnt;

    void getAnalysisUsage(AnalysisUsage &AU) const;

    bool runOnModule(llvm::Module &M);
    static char ID;

    // initiate a function for a specific call instruction
    void initiateFunctionFor(Instruction *CallI, Function *F);

    // collect the information of call instructions, mainly for checking the necessity of initiation
    void collectCallInsts(llvm::Module *M);

    void insertFunctionShouldBeInitiated(Function *F);

    void BFS_check_and_initiate(Function *startF, llvm::Module *M);

    void checkAndInitiateCallInstIn(Function *curF);

    void pushCalledFunctionIntoQueue(Function *curF, std::queue<Function *> &FuncQ,
                                     std::set<Function *> &FuncVisited);

    bool ShouldFunctionBeInitiated(Function *F);

    int newFuncID(Function *F);

    void replaceCallInst(Instruction *callI, Function *F);

    void BFS_checkDeadFunction(Function *startF, llvm::Module *M);

    std::error_code ErrInfo;
    raw_ostream *FuncInitiationLog;

    raw_string_ostream *tmp_stream;
    std::string tmp_stream_str;

    /// Timer

    struct timeval tv_begin;
    struct timeval tv_end;
};

#endif

    HI_FunctionInstantiation(const char *FuncInitiationLog_Name, const std::string _topFunctionName)
        : ModulePass(ID)
    {
        topFunctionName = _topFunctionName;


#ifndef _HI_MulOrderOpt
#define _HI_MulOrderOpt
// related headers should be included.
#include "HI_print.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/Local.h"
#include <cstdlib>
#include <ios>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/time.h>
#include <vector>
using namespace llvm;

class HI_MulOrderOpt : public FunctionPass
{
  public:
    HI_MulOrderOpt(const char *MulOrderOptLog_Name) : FunctionPass(ID)
    {
        Instruction_Counter = 0;
        Function_Counter = 0;
        BasicBlock_Counter = 0;
        Loop_Counter = 0;
        callCounter = 0;
        MulOrderOptLog = new raw_fd_ostream(MulOrderOptLog_Name, ErrInfo, sys::fs::F_None);
        tmp_stream = new raw_string_ostream(tmp_stream_str);
    } // define a pass, which can be inherited from ModulePass, LoopPass, FunctionPass and etc.

    ~HI_MulOrderOpt()
    {

        MulOrderOptLog->flush();
        delete MulOrderOptLog;
        tmp_stream->flush();
        delete tmp_stream;
    }

    virtual bool doInitialization(llvm::Module &M)
    {
        print_status("Initilizing HI_MulOrderOpt pass.");

        return false;
    }

    struct cmp_mulorder;

    // virtual bool doFinalization(Module &M)
    // {
    //     return false;
    // }

    void getAnalysisUsage(AnalysisUsage &AU) const;
    virtual bool runOnFunction(llvm::Function &F);

    void recursiveGetMulOpAndCounter(Value *MulI);

    Value *recursiveMul(std::priority_queue<std::pair<int, Value *>,
                                            std::vector<std::pair<int, Value *>>, cmp_mulorder>
                            cur_heap,
                        int tot_cnt, IRBuilder<> &Builder);

    int callCounter;
    int Instruction_Counter;
    int Function_Counter;
    int BasicBlock_Counter;
    int Loop_Counter;

    Function *TargeFunction;

    struct cmp_mulorder
    {
        bool operator()(std::pair<int, Value *> a, std::pair<int, Value *> b)
        {
            if (a.first == b.first)
                return a.second < b.second;
            return a.first < b.first;
        }
    };

    std::priority_queue<std::pair<int, Value *>, std::vector<std::pair<int, Value *>>, cmp_mulorder>
        heap_opCnt;
    std::map<MulOperator *, std::map<Value *, int>> op2Cnt;
    std::set<Value *> generatedI;

    std::error_code ErrInfo;
    raw_ostream *MulOrderOptLog;

    raw_string_ostream *tmp_stream;
    std::string tmp_stream_str;

    /// Timer

    struct timeval tv_begin;
    struct timeval tv_end;
};

#endif


#ifndef _HI_ClockInfo
#define _HI_ClockInfo

#include <string>

const int clockNum = 15;

extern const std::string clockStrs[100];


#ifndef _HI_SYSEXEC
#define _HI_SYSEXEC

#include <array>
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <sys/time.h>


#ifndef _Gobal_Confic_Parse
#define _Gobal_Confic_Parse

#include "HI_StringProcess.h"
#include "HI_SysExec.h"
#include "HI_print.h"
#include <assert.h>
#include <iostream>
#include <map>
#include <string>

extern std::string clock_period_str;
extern std::string HLS_lib_path;

void Parse_Config(const char *config_file_name, std::map<std::string, int> &LoopLabel2UnrollFactor);

void parseLoopUnroll(std::stringstream &iss, std::map<std::string, int> &LoopLabel2UnrollFactor);

#endif


#include "HI_ArraySensitiveToLoopLevel.h"
#include "HI_print.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Pass.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"

#include <ios>
#include <stdio.h>
#include <stdlib.h>
#include <string>

using namespace llvm;

/*
    find the longest path in the function as its latency
    also record the resource cost in the function
*/
// HI_ArraySensitiveToLoopLevel::timingBase HI_ArraySensitiveToLoopLevel::analyzeFunction(Function
// *F)
// {
//     if (FunctionLatency.find(F)!=FunctionLatency.end())
//         return FunctionLatency[F]*1;

//     if (DEBUG) *Evaluating_log << "Evaluating the latency of Function " << F->getName() <<":\n";
//     BasicBlock *Func_Entry = &F->getEntryBlock(); //get the entry of the function
//     if (DEBUG) *Evaluating_log << "-- its entry is: " << Func_Entry->getName() <<"\n";

//     // F->getA

//     // (2) traverse the block in loop by DFS to find the longest path
//     timingBase max_critial_path_in_F(0,0,1,clock_period);
//     timingBase origin_path_in_F(0,0,1,clock_period);
//     resourceBase resourceAccumulator(0,0,0,clock_period);

//     tmp_BlockCriticalPath_inFunc.clear(); // record the block level critical path in the loop
//     tmp_LoopCriticalPath_inFunc.clear(); // record the critical path to the end of sub-loops in
//     the loop

//     Func_BlockVisited.clear();
//     analyzeFunction_traverseFromEntryToExiting(origin_path_in_F, F, Func_Entry,
//     resourceAccumulator);

//     for (auto tmp_it : tmp_BlockCriticalPath_inFunc)
//         if (tmp_it.second > max_critial_path_in_F)
//             max_critial_path_in_F = tmp_it.second;
//     for (auto tmp_it : tmp_LoopCriticalPath_inFunc)
//         if (tmp_it.second > max_critial_path_in_F)
//             max_critial_path_in_F = tmp_it.second;

//     // analyze the cost of floating-point operators (most of them will be aggressive reused by
//     VivadoHLS binding) resourceAccumulator = resourceAccumulator +
//     costRescheduleFPDSPOperators_forFunction(F);

//     // (4) mark the blocks in loop with the loop latency, so later processing can regard this
//     loop as an integration Func_BlockVisited.clear(); FunctionLatency[F] = max_critial_path_in_F;
//     FunctionResource[F] = resourceAccumulator;
//     FunctionEvaluated.insert(F);

//     if (DEBUG) *Evaluating_log << "\n\n\nDone evaluation Function Latency for Function " <<
//     F->getName() << " and its latency is " << max_critial_path_in_F <<" cycles and its resource
//     cost is: " << resourceAccumulator << ".\n"; if (DEBUG) *Evaluating_log << "---- Function " <<
//     F->getName() << " includes "<<Function2OuterLoops[F].size()<<" following most outer
//     loop(s):\n-------"; for (auto tmpL : Function2OuterLoops[F])
//     {
//         if (DEBUG) *Evaluating_log << " Loop:" << tmpL->getName() << "(lat=" <<
//         LoopLatency[tmpL->getHeader()] << ") ";
//     }
//     if (DEBUG) *Evaluating_log << "\n---- Function " << F->getName() << " includes following
//     block(s) out of loops:\n-------"; for (auto &B_it : *F)
//     {
//         if (!isInLoop(&B_it))
//             if (DEBUG) *Evaluating_log << " Block:" << B_it.getName() << "(lat=" <<
//             BlockLatency[&B_it] << ") ";
//     }
//     if (DEBUG) *Evaluating_log << "\n\n\n\n";
//     return max_critial_path_in_F*1;

// }

/*
    traverse the block in function by DFS to find the longest paths to each block in the function:
    (1) Mark the block visited, as a step of typical DFS
    (2) Check whether the search reaches a block in the loops
    (3a) -- If it is a block in loops, regard the most outer loop as intergration and update the
   critical path if necessary (max(ori_CP, lastStateCP + LoopLatency)).
         -- find the successors of the loop by checking its exiting blocks' successors and continue
   the DFS (3b) -- If it is a block out of loops, evaluate the block latency and update the critical
   path if necessary (max(ori_CP, lastStateCP + BlockLatency)).
         -- find the successors of the block and continue the DFS
    (4) Release the block from visited flag, as a step of typical DFS
*/

// void
// HI_ArraySensitiveToLoopLevel::analyzeFunction_traverseFromEntryToExiting(HI_ArraySensitiveToLoopLevel::timingBase
// tmp_critical_path, Function *F, BasicBlock* curBlock, HI_ArraySensitiveToLoopLevel::resourceBase
// &resourceAccumulator)
// {

//     // (1) Mark the block visited, as a step of typical DFS
//     Func_BlockVisited.insert(curBlock);

//     if (DEBUG) *Evaluating_log << "---- FUNCTION traverser arrive Block: " << curBlock->getName()
//     <<" ";

//     // (2) Check whether the search reaches a block in the loops
//     if (isInLoop(curBlock))
//     {
//         // (3a) -- If it is a block in loops, regard the loop as intergration and update the
//         critical path if necessary (max(ori_CP, lastStateCP + LoopLatency)). Loop* tmp_OuterLoop
//         = getOuterLoopOfBlock(curBlock); if (DEBUG) *Evaluating_log << "---- Block: " <<
//         curBlock->getName() <<" is in Outer Loop : "<< tmp_OuterLoop->getName() <<" ";

//         if (Function2OuterLoops.find(F) == Function2OuterLoops.end())
//         {
//             std::vector<Loop*> tmp_vec_loop;
//             Function2OuterLoops[F] = tmp_vec_loop;
//         }

//         timingBase latency_Loop = analyzeOuterLoop(tmp_OuterLoop); // treat the entire loop as a
//         block node and get the latency timingBase try_critical_path = tmp_critical_path +
//         latency_Loop;  // first, get the critical path to the end of loop

//         if (DEBUG) *Evaluating_log << " LoopLatency =  " << latency_Loop <<" ";
//         if (DEBUG) *Evaluating_log << " NewCP =  " << try_critical_path <<" ";
//         bool checkFlag = false;

//         if (tmp_LoopCriticalPath_inFunc.find(tmp_OuterLoop) == tmp_LoopCriticalPath_inFunc.end()
//         )
//         {
//             resourceAccumulator = resourceAccumulator + LoopResource[tmp_OuterLoop->getHeader()]
//             ; checkFlag = true;
//         }
//         else if (try_critical_path > tmp_LoopCriticalPath_inFunc[tmp_OuterLoop]) checkFlag =
//         true;

//         if (checkFlag)
//         {
//             if (tmp_LoopCriticalPath_inFunc.find(tmp_OuterLoop) !=
//             tmp_LoopCriticalPath_inFunc.end())
//             {
//                 if (DEBUG) *Evaluating_log << " OriCP =  " <<
//                 tmp_LoopCriticalPath_inFunc[tmp_OuterLoop] <<"\n";
//             }
//             else
//             {
//                 Function2OuterLoops[F].push_back(tmp_OuterLoop);
//                 if (DEBUG) *Evaluating_log << " No OriCP" <<"\n";
//             }
//             tmp_LoopCriticalPath_inFunc[tmp_OuterLoop] = try_critical_path;

//              //  (3a)  -- find the successors of the loop by checking its exiting blocks'
//              successors and continue the DFS
//             SmallVector<BasicBlock*, 8>  tmp_OuterLoop_ExitingBlocks;
//             tmp_OuterLoop->getExitingBlocks(tmp_OuterLoop_ExitingBlocks);
//             for (auto ExitB : tmp_OuterLoop_ExitingBlocks)
//             {
//                 for (auto B : successors(ExitB))
//                 {
//                     if (F == B->getParent() && Func_BlockVisited.find(B) ==
//                     Func_BlockVisited.end() && !tmp_OuterLoop->contains(B))
//                     {
//                         if (DEBUG) *Evaluating_log << "---- function continue to traverser to
//                         Block: " << B->getName()  << " from " << curBlock->getName() <<"\n";
//                         analyzeFunction_traverseFromEntryToExiting(try_critical_path, F, B,
//                         resourceAccumulator);
//                     }
//                 }
//             }

//         }

//     }
//     else
//     {
//         //     (3b) -- If it is a block out of loops, evaluate the block latency and update the
//         critical path if necessary (max(ori_CP, lastStateCP + BlockLatency)). if (DEBUG)
//         *Evaluating_log << "---- Block: " << curBlock->getName() <<" is NOT in any Outer Loop ";
//         timingBase latency_CurBlock = BlockLatencyResourceEvaluation(curBlock); // first, get the
//         latency of the current block timingBase try_critical_path = tmp_critical_path +
//         latency_CurBlock; if (DEBUG) *Evaluating_log << "---- latencyBlock =  " <<
//         latency_CurBlock <<" "; if (DEBUG) *Evaluating_log << " NewCP =  " << try_critical_path
//         <<" "; bool checkFlag = false;

//         if (tmp_BlockCriticalPath_inFunc.find(curBlock) == tmp_BlockCriticalPath_inFunc.end() )
//         {
//             resourceAccumulator = resourceAccumulator + BlockResource[curBlock] ;
//             checkFlag = true;
//         }
//         else if (try_critical_path > tmp_BlockCriticalPath_inFunc[curBlock]) checkFlag = true;

//         if (checkFlag) // update the block-level critical path
//         {
//             if (tmp_BlockCriticalPath_inFunc.find(curBlock) !=
//             tmp_BlockCriticalPath_inFunc.end())
//             {
//                 if (DEBUG) *Evaluating_log << " OriCP =  " <<
//                 tmp_BlockCriticalPath_inFunc[curBlock] <<"\n";
//             }
//             else
//             {
//                 if (DEBUG) *Evaluating_log << " No OriCP" <<"\n";
//             }

//             tmp_BlockCriticalPath_inFunc[curBlock] = try_critical_path;

//             // (3b)  -- find the successors of the block and continue the DFS
//             for (auto B : successors(curBlock))
//             {
//                 if (F == B->getParent() && Func_BlockVisited.find(B) == Func_BlockVisited.end())
//                 {
//                     if (DEBUG) *Evaluating_log << "---- function continue to traverser to Block:
//                     " << B->getName() << " from " << curBlock->getName() <<" ";
//                     analyzeFunction_traverseFromEntryToExiting(try_critical_path, F, B,
//                     resourceAccumulator);
//                 }
//             }
//         }
//     }
//     // (4) Release the block from visited flag, as a step of typical DFS
//     Func_BlockVisited.erase(curBlock);
// }

// // get how many state needed for the function
// int HI_ArraySensitiveToLoopLevel::getFunctionStageNum(HI_ArraySensitiveToLoopLevel::timingBase
// tmp_critical_path, Function *F, BasicBlock* curBlock)
// {

//     // // (1) Mark the block visited, as a step of typical DFS
//     // Func_BlockVisited.insert(curBlock);
//     // timingBase latency_CurBlock = BlockLatencyResourceEvaluation(curBlock); // first, get the
//     latency of the current block

//     // std::string Block_name = curBlock->getName();

//     // for (auto &I : *curBlock)
//     // {
//     //     if (auto call_I = dyn_cast<CallInst>(&I))
//     //     {
//     //         latency_CurBlock = latency_CurBlock -
//     FunctionLatency[call_I->getCalledFunction()]; latency_CurBlock.latency++;
//     //     }
//     // }

//     // timingBase try_critical_path = tmp_critical_path + latency_CurBlock*1;

//     // // (2) get the longest STATE path from entry to the specific block
//     // int CP = (try_critical_path*1).latency;
//     // for (auto B : successors(curBlock))
//     // {
//     //     if (F == B->getParent() && Func_BlockVisited.find(B) == Func_BlockVisited.end())
//     //     {
//     //         int tmp = getFunctionStageNum(try_critical_path*1, F, B);
//     //         if (tmp > CP)
//     //             CP = tmp;
//     //     }
//     // }

//     // Func_BlockVisited.erase(curBlock);

//     // return CP;

//     int res = 0;
//     for (auto &B : *F)
//     {
//         res = res + getStageNumOfBlock(&B);
//     }
//     return res;
// }

// return the resource cost of the function
HI_ArraySensitiveToLoopLevel::resourceBase
HI_ArraySensitiveToLoopLevel::getFunctionResource(Function *F)
{
    if (F->getName().find("llvm.") != std::string::npos ||
        F->getName().find("HIPartitionMux") !=
            std::string::npos) // bypass the "llvm.xxx" functions..
        return resourceBase(0, 0, 0, clock_period);
    return FunctionResource[F];
}

HI_ArraySensitiveToLoopLevel::resourceBase
HI_ArraySensitiveToLoopLevel::costRescheduleFPDSPOperators_forFunction(Function *F)
{
    resourceBase res(0, 0, 0, clock_period);

    std::vector<std::string> checkopcodes = {"fmul", "fadd", "fdiv", "fsub",
                                             "dmul", "dadd", "ddiv", "dsub"};

    // Block2FPDSPReuseScheduleUnits[I->getParent()][opcode].push_back(schUnit);
    for (auto cur_opcode : checkopcodes)
    {
        int op_totalcnt = 0;

        int max_cnt = 0;
        for (auto &cur_block : *F)
        {

            if (Block2FPDSPOpCnt.find(tmp_block) == Block2FPDSPOpCnt.end())
                continue;
            if (Block2FPDSPOpCnt[tmp_block].find(cur_opcode) == Block2FPDSPOpCnt[tmp_block].end())
                continue;

            if (max_cnt < Block2FPDSPOpCnt[tmp_block][cur_opcode])
                max_cnt = Block2FPDSPOpCnt[tmp_block][cur_opcode];
        }

        if (DEBUG)
            *Evaluating_log << "  for function: the amount of floating point operators (refI):"
                            << cur_opcode << " is " << max_cnt << " each cost =["
                            << checkFPOperatorCost(cur_opcode) << "]\n";

        res = res + checkFPOperatorCost(cur_opcode) * max_cnt;
    }

    return res;
}


#include "HI_MulOrderOpt.h"
#include "HI_print.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Pass.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/Local.h"
#include <ios>
#include <stdio.h>
#include <stdlib.h>
#include <string>

using namespace llvm;

bool HI_MulOrderOpt::runOnFunction(
    llvm::Function &F) // The runOnModule declaration will overide the virtual one in ModulePass,
                       // which will be executed for each Module.
{
    print_status("Running HI_MulOrderOpt pass.");
    std::set<long long> power2;
    generatedI.clear();
    op2Cnt.clear();

    for (int i = 0, pow = 1; i < 64; i++, pow *= 2)
        power2.insert(pow);

    if (F.getName().find("llvm.") != std::string::npos)
        return false;

    // AAAAABB
    // AAAAA * BB
    // AA * AA * A   B *  B
    //  A * A
    bool changed = 0;
    for (BasicBlock &B : F)
    {
        bool action = 1;
        while (action)
        {
            action = 0;
            for (BasicBlock::reverse_iterator it = B.rbegin(), ie = B.rend(); it != ie; it++)
            {
                Instruction *I = &*it;

                if (I->getOpcode() != Instruction::Mul)
                    continue;
                if (generatedI.find(I) !=
                    generatedI.end()) // this Instruction in generated by us, bypass it
                    continue;
                MulOperator *curMulI = dyn_cast<MulOperator>(I);
                MulOperator *LMul = nullptr, *RMul = nullptr;
                Value *VarForMul;

                LMul = dyn_cast<MulOperator>(I->getOperand(0));
                RMul = dyn_cast<MulOperator>(I->getOperand(1));
                if (!LMul && !RMul)
                    continue;

                while (heap_opCnt.size() > 0)
                    heap_opCnt.pop();
                op2Cnt.clear();
                recursiveGetMulOpAndCounter(I);

                int tot_cnt = 0;
                for (auto total_val_cnt_pair : op2Cnt[curMulI])
                {
                    heap_opCnt.push(std::pair<int, Value *>(total_val_cnt_pair.second,
                                                            total_val_cnt_pair.first));
                    tot_cnt += total_val_cnt_pair.second;
                }

                changed = 1;
                action = 1;

                *MulOrderOptLog << "\n\nbefore replacement:\n\n" << B;
                IRBuilder<> Builder(I);

                int replcaceCnt = 0;

                Value *newMul = recursiveMul(heap_opCnt, tot_cnt, Builder);
                curMulI->replaceAllUsesWith(newMul);
                RecursivelyDeleteTriviallyDeadInstructions(curMulI);

                *MulOrderOptLog << "\n\nafter replacement#" << replcaceCnt << ":\n\n" << B;
                // while (1)
                // {
                //     replcaceCnt++;
                //     int mulcnt = (heap_opCnt.top()).first;
                //     if (mulcnt>1)
                //     {
                //         *MulOrderOptLog << "\n\n========================\n  handling " <<
                //         *heap_opCnt.top().second << "^(" << mulcnt << ")\n";
                //         MulOrderOptLog->flush();
                //         Value *newMul =
                //         heap_opCnt.pop();
                //         heap_opCnt.push(std::pair<int, Value*>(1, newMul));
                //         *MulOrderOptLog << "\n\nafter replacement#" << replcaceCnt <<":\n\n" <<
                //         B;
                //     }
                //     else
                //     {
                //         if (heap_opCnt.size()==1)
                //         {
                //             *MulOrderOptLog << "\n\n========================\n  do the
                //             re-multiplication at " << *heap_opCnt.top().second << "\n";
                //             MulOrderOptLog->flush();
                //             curMulI->replaceAllUsesWith(heap_opCnt.top().second);
                //             RecursivelyDeleteTriviallyDeadInstructions(curMulI);
                //             *MulOrderOptLog << "\n\nafter replacement#" << replcaceCnt <<":\n\n"
                //             << B; break;
                //         }
                //         else
                //         {
                //             Value *val0 = heap_opCnt.top().second;
                //             heap_opCnt.pop();
                //             Value *val1 = heap_opCnt.top().second;
                //             heap_opCnt.pop();
                //             Value *newMul = Builder.CreateMul(val0, val1);
                //             generatedI.insert(newMul);
                //             *MulOrderOptLog << "\n\n========================\n  do the
                //             re-multiplication between " << *val0 << " and " << *val1 << " by " <<
                //             *newMul << "\n"; heap_opCnt.push(std::pair<int, Value*>(1, newMul));
                //             *MulOrderOptLog << "\n\nafter replacement#" << replcaceCnt <<":\n\n"
                //             << B;
                //         }
                //     }
                // }

                break;

                if (action)
                    break;
            }
        }
    }
    MulOrderOptLog->flush();
    return changed;
}

char HI_MulOrderOpt::ID =
    0; // the ID for pass should be initialized but the value does not matter, since LLVM uses the
       // address of this variable as label instead of its value.

void HI_MulOrderOpt::getAnalysisUsage(AnalysisUsage &AU) const
{
    AU.addRequired<DominatorTreeWrapperPass>();
    AU.addRequired<ScalarEvolutionWrapperPass>();
    AU.addRequired<LoopInfoWrapperPass>();
    AU.setPreservesCFG();
}

void HI_MulOrderOpt::recursiveGetMulOpAndCounter(Value *MulI)
{
    if (auto real_MulI = dyn_cast<MulOperator>(MulI))
    {
        if (op2Cnt.find(real_MulI) != op2Cnt.end())
            return;
        std::map<Value *, int> tmp_val_cnt_map;

        {
            MulOperator *opMul = dyn_cast<MulOperator>(real_MulI->getOperand(i));
            if (opMul)
            {
                recursiveGetMulOpAndCounter(opMul);
                for (auto val_cnt_pair : op2Cnt[opMul])
                {
                    if (tmp_val_cnt_map.find(val_cnt_pair.first) == tmp_val_cnt_map.end())
                    {
                        tmp_val_cnt_map[val_cnt_pair.first] = val_cnt_pair.second;
                    }
                    else
                    {
                        tmp_val_cnt_map[val_cnt_pair.first] += val_cnt_pair.second;
                    }
                }
            }
            else
            {
                if (tmp_val_cnt_map.find(real_MulI->getOperand(i)) == tmp_val_cnt_map.end())
                {
                    tmp_val_cnt_map[real_MulI->getOperand(i)] = 1;
                }
                else
                {
                    tmp_val_cnt_map[real_MulI->getOperand(i)] += 1;
                }
            }
        }

        op2Cnt[real_MulI] = tmp_val_cnt_map;
        *MulOrderOptLog << "\n=========================\nop info for MulI: " << *MulI << "\n";
        for (auto val_cnt_pair : tmp_val_cnt_map)
        {
            *MulOrderOptLog << "     -->" << *val_cnt_pair.first << "  --> " << val_cnt_pair.second
                            << "\n";
        }
    }

    MulOrderOptLog->flush();
}

Value *HI_MulOrderOpt::recursiveMul(
    std::priority_queue<std::pair<int, Value *>, std::vector<std::pair<int, Value *>>,
                        HI_MulOrderOpt::cmp_mulorder>
        cur_heap,
    int tot_cnt, IRBuilder<> &Builder)
{
    std::priority_queue<std::pair<int, Value *>, std::vector<std::pair<int, Value *>>, cmp_mulorder>
        cur_heapL, cur_heapR;
    int cntL = (tot_cnt + 1) / 2;
    int cntR = tot_cnt - cntL;

    int const_cntL = cntL;
    int const_cntR = cntR;

    if (tot_cnt == 1)
        return cur_heap.top().second;

    while (cur_heap.size())
    {
        std::pair<int, Value *> curMuls = cur_heap.top();

        cur_heap.pop();

        *MulOrderOptLog << " spliting " << curMuls.first << " x [" << *curMuls.second << "]\n";

        if (curMuls.first <= cntL)
        {
            cur_heapL.push(curMuls);
            cntL -= curMuls.first;
            *MulOrderOptLog << "       assign " << curMuls.first << " in left heap (newcap=" << cntL
                            << ")\n";
        }
        else
        {
            if (cntL)
            {
                cur_heapL.push(std::pair<int, Value *>(cntL, curMuls.second));
                *MulOrderOptLog << "       assign " << cntL << " in left heap (newcap=" << 0
                                << ")\n";
                curMuls.first -= cntL;
                cntL = 0;
            }

            cur_heapR.push(std::pair<int, Value *>(curMuls.first, curMuls.second));
            cntR -= (curMuls.first);
            *MulOrderOptLog << "       assign " << curMuls.first
                            << " in right heap (newcap=" << cntR << ")\n";
        }
        assert(cntL >= 0);
        assert(cntR >= 0);
    }

    *MulOrderOptLog << "\n\n\n\n\n";
    Value *subMul0 = recursiveMul(cur_heapL, const_cntL, Builder);
    Value *subMul1 = recursiveMul(cur_heapR, const_cntR, Builder);
    Value *newMul = Builder.CreateMul(subMul0, subMul1);
    generatedI.insert(newMul);
    return newMul;
}

for (int i = 0; i < real_MulI->getNumOperands(); i++)


#include "LLVM_exp13b_HI_ReplaceSelectAccess.h"

using namespace llvm;
using namespace polly;

int main(int argc, char **argv)
{

    // Compile the source code into IR and Parse the input LLVM IR file into a module
    SMDiagnostic Err;
    LLVMContext Context;
    std::string cmd_str = "clang -O1 -emit-llvm -g -S " + std::string(argv[1]) + " -o top.bc 2>&1";
    std::string top_str = std::string(argv[2]);
    std::string configFile_str = std::string(argv[3]);
    print_cmd(cmd_str.c_str());
    bool result = sysexec(cmd_str.c_str());
    assert(result); // ensure the cmd is executed successfully
                    // system(cmd_str.c_str());

    std::unique_ptr<Module> Mod(parseIRFile("top.bc", Err, Context));
    if (!Mod)
    {
        Err.print(argv[0], errs());
        return 1;
    }

    // Create a pass manager and fill it with the passes we want to run.
    legacy::PassManager PM, PM1;
    LLVMTargetRef T;
    ModulePassManager MPM;

    char *Error;

    if (LLVMGetTargetFromTriple((Mod->getTargetTriple()).c_str(), &T, &Error))
    {
        print_error(Error);
    }
    else
    {
        std::string targetname = LLVMGetTargetName(T);
        targetname = "The target machine is: " + targetname;
        print_info(targetname.c_str());
    }

    Triple ModuleTriple(Mod->getTargetTriple());
    TargetLibraryInfoImpl TLII(ModuleTriple);
    PM1.add(new TargetLibraryInfoWrapperPass(TLII));

    print_info("Enable LoopSimplify Pass");
    auto loopsimplifypass = createLoopSimplifyPass();
    PM1.add(loopsimplifypass);

    auto indvarsimplifypass = createIndVarSimplifyPass();
    PM1.add(indvarsimplifypass);
    print_info("Enable IndVarSimplifyPass Pass");

    PM1.add(createTargetTransformInfoWrapperPass(TargetIRAnalysis()));
    print_info("Enable TargetIRAnalysis Pass");

    auto hi_separateconstoffsetfromgep =
        new HI_SeparateConstOffsetFromGEP("HI_SeparateConstOffsetFromGEP", true);
    PM1.add(hi_separateconstoffsetfromgep);
    print_info("Enable HI_SeparateConstOffsetFromGEP Pass");
    // auto separateconstoffsetfromgep = createSeparateConstOffsetFromGEPPass(true);
    // PM.add(separateconstoffsetfromgep);
    // print_info("Enable SeparateConstOffsetFromGEP Pass");

    if (argc == 5 && std::string(argv[4]) == "enable-lsr")
    {
        auto loopstrengthreducepass = createLoopStrengthReducePass();
        PM1.add(loopstrengthreducepass);
        print_info("Enable LoopStrengthReducePass Pass");
    }

    auto hi_duplicateinstrm = new HI_DuplicateInstRm("rmInsts");
    PM1.add(hi_duplicateinstrm);
    print_info("Enable HI_DuplicateInstRm Pass");

    auto hi_aggressivelsr_mul = new HI_AggressiveLSR_MUL("AggressiveLSR");
    PM.add(hi_aggressivelsr_mul);
    print_info("Enable HI_AggressiveLSR_MUL Pass");

    // auto lazyvalueinfowrapperpass = new LazyValueInfoWrapperPass();
    // PM.add(lazyvalueinfowrapperpass);
    // print_info("Enable LazyValueInfoWrapperPass Pass");

    auto hi_varwidthreduce = new HI_VarWidthReduce("VarWidth");
    PM1.add(hi_varwidthreduce);
    print_info("Enable HI_VarWidthReduce Pass");

    auto hi_intstructionmovebackward =
        new HI_IntstructionMoveBackward("HI_IntstructionMoveBackward");
    PM1.add(hi_intstructionmovebackward);
    print_info("Enable HI_IntstructionMoveBackward Pass");

    // don't remove chained operations
    auto hi_hlsduplicateinstrm = new HI_HLSDuplicateInstRm("HLSrmInsts");
    PM1.add(hi_hlsduplicateinstrm);
    print_info("Enable HI_HLSDuplicateInstRm Pass");

    // auto hi_arrayaccesspattern = new HI_ArrayAccessPattern("HI_ArrayAccessPattern",top_str);
    // PM.add(hi_arrayaccesspattern);
    // print_info("Enable HI_ArrayAccessPattern Pass");

    auto hi_functioninstantiation =
        new HI_FunctionInstantiation("HI_FunctionInstantiation", top_str);
    PM1.add(hi_functioninstantiation);
    print_info("Enable HI_FunctionInstantiation Pass");

    auto hi_replaceselectaccess = new HI_ReplaceSelectAccess("HI_ReplaceSelectAccess");
    PM1.add(hi_replaceselectaccess);
    print_info("Enable HI_ReplaceSelectAccess Pass");
    // PM.add(createCorrelatedValuePropagationPass());
    // print_info("Enable CorrelatedValuePropagation Pass");

    // PM.add(createCorrelatedValuePropagationPass());
    // print_info("Enable CorrelatedValuePropagation Pass");

    PM1.run(*Mod);

    std::error_code EC;
    llvm::raw_fd_ostream OS1("top_output0.bc", EC, llvm::sys::fs::F_None);
    WriteBitcodeToFile(*Mod, OS1);
    OS1.flush();

    // auto hi_duplicateinstrm1 = new HI_DuplicateInstRm("rmInsts");
    // PM.add(hi_duplicateinstrm1);
    // print_info("Enable HI_DuplicateInstRm Pass");

    // PM.add(createStraightLineStrengthReducePass());
    // print_info("Enable StraightLineStrengthReduce Pass");

    // auto instructioncombiningpass = createInstructionCombiningPass(true);
    // PM.add(instructioncombiningpass);
    // print_info("Enable InstructionCombiningPass Pass");

    // auto loopstrengthreducepass = createLoopStrengthReducePass();
    // PM.add(loopstrengthreducepass);
    // print_info("Enable LoopStrengthReducePass Pass");

    auto loopinfowrapperpass = new LoopInfoWrapperPass();
    PM.add(loopinfowrapperpass);
    print_info("Enable LoopInfoWrapperPass Pass");

    auto regioninfopass = new RegionInfoPass();
    PM.add(regioninfopass);
    print_info("Enable RegionInfoPass Pass");

    auto scalarevolutionwrapperpass = new ScalarEvolutionWrapperPass();
    PM.add(scalarevolutionwrapperpass);
    print_info("Enable ScalarEvolutionWrapperPass Pass");

    auto loopaccesslegacyanalysis = new LoopAccessLegacyAnalysis();
    PM.add(loopaccesslegacyanalysis);
    print_info("Enable LoopAccessLegacyAnalysis Pass");

    auto dominatortreewrapperpass = new DominatorTreeWrapperPass();
    PM.add(dominatortreewrapperpass);
    print_info("Enable DominatorTreeWrapperPass Pass");

    auto optimizationremarkemitterwrapperpass = new OptimizationRemarkEmitterWrapperPass();
    PM.add(optimizationremarkemitterwrapperpass);
    print_info("Enable OptimizationRemarkEmitterWrapperPass Pass");

    auto aaresultswrapperpass = new AAResultsWrapperPass();
    print_info("Enable AAResultsWrapperPass Pass");
    PM.add(aaresultswrapperpass);

    auto scopdetectionwrapperpass = new ScopDetectionWrapperPass();
    print_info("Enable ScopDetectionWrapperPass Pass");
    PM.add(scopdetectionwrapperpass);

    auto assumptioncachetracker = new AssumptionCacheTracker();
    print_info("Enable AssumptionCacheTracker Pass");
    PM.add(assumptioncachetracker);

    auto scopinfowrapperpass = new ScopInfoWrapperPass();
    print_info("Enable ScopInfoWrapperPass Pass");
    PM.add(scopinfowrapperpass);

    auto scopinforegionpass = new ScopInfoRegionPass();
    print_info("Enable ScopInfoRegionPass Pass");
    PM.add(scopinforegionpass);

    auto dependenceinfowrapperpass = new DependenceInfoWrapperPass();
    print_info("Enable DependenceInfoWrapperPass Pass");
    PM.add(dependenceinfowrapperpass);

    auto polyhedralinfo = new PolyhedralInfo();
    print_info("Enable PolyhedralInfo Pass");
    PM.add(polyhedralinfo);

    auto hi_polly_info = new HI_Polly_Info("PollyInformation");
    print_info("Enable PollyInformation Pass");
    PM.add(hi_polly_info);

    auto hi_loopinformationcollect = new HI_LoopInFormationCollect("Loops");
    PM.add(hi_loopinformationcollect);
    print_info("Enable HI_LoopInFormationCollect Pass");

    auto hi_loopdependenceanalysis = new HI_LoopDependenceAnalysis("HI_LoopDependenceAnalysis");
    print_info("Enable HI_LoopDependenceAnalysis Pass");
    PM.add(hi_loopdependenceanalysis);

    // auto hi_simpletimingevaluation = new
    // HI_SimpleTimingEvaluation("HI_SimpleTimingEvaluation",top_str.c_str()); print_info("Enable
    // HI_SimpleTimingEvaluation Pass"); PM.add(hi_simpletimingevaluation);

    auto hi_nodirectivetimingresourceevaluation = new HI_NoDirectiveTimingResourceEvaluation(
        configFile_str.c_str(), "HI_NoDirectiveTimingResourceEvaluation", "BRAM_info",
        top_str.c_str());
    print_info("Enable HI_NoDirectiveTimingResourceEvaluation Pass");
    PM.add(hi_nodirectivetimingresourceevaluation);

    print_info("Enable HI_FindFunctions Pass");
    print_info("Enable HI_DependenceList Pass");
    auto hi_findfunction = new HI_FindFunctions();
    PM.add(hi_findfunction);
    auto hi_dependencelist = new HI_DependenceList("Instructions", "Instruction_Dep");
    PM.add(hi_dependencelist);

    // while (1)
    // {
    //     int opBitWid, outBitWid;
    //     std::string ClockPerid,opcode;
    //     std::cin >> opcode >> opBitWid >> outBitWid >> ClockPerid;
    //     if (opcode=="end")
    //       break;
    //     // int DSP = testObj->get_N_DSP(opcode, opBitWid , outBitWid, ClockPerid);
    //     // int FF = testObj->get_N_FF(opcode, opBitWid , outBitWid, ClockPerid);
    //     // int LUT = testObj->get_N_LUT(opcode, opBitWid , outBitWid, ClockPerid);
    //     // int Lat = testObj->get_N_Lat(opcode, opBitWid , outBitWid, ClockPerid);
    //     // double Delay = testObj->get_N_Delay(opcode, opBitWid , outBitWid, ClockPerid);
    //     hi_nodirectivetimingresourceevaluation->get_inst_info(opcode, opBitWid , outBitWid,
    //     ClockPerid).print();
    // }

    print_status("Start LLVM processing");
    PM.run(*Mod);
    print_status("Accomplished LLVM processing");

    assert(hi_nodirectivetimingresourceevaluation->topFunctionFound &&
           "The specified top function is not found in the program");

    print_status("Writing LLVM IR to File");

    llvm::raw_fd_ostream OS("top_output.bc", EC, llvm::sys::fs::F_None);
    WriteBitcodeToFile(*Mod, OS);
    OS.flush();

    print_status("Translate the IR to be readable one");
    cmd_str = "llvm-dis top_output.bc 2>&1";
    print_cmd(cmd_str.c_str());
    result = sysexec(cmd_str.c_str());
    assert(result); // ensure the cmd is executed successfully
    return 0;
}

    if (argc < 4)
    {
        errs() << "Usage: " << argv[0] << " <C/C++ file> <Top_Function_Name> <Config_File_Path>\n";
        return 1;
    }


#include "LLVM_exp3_loop_info_extraction.h"

using namespace llvm;

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        errs() << "Usage: " << argv[0] << " <C/C++ file>\n";
        return 1;
    }

    // Compile the source code into IR and Parse the input LLVM IR file into a module
    SMDiagnostic Err;
    LLVMContext Context;
    std::string cmd_str = "clang -O1 -emit-llvm -S " + std::string(argv[1]) + " -o top.bc 2>&1";
    print_cmd(cmd_str.c_str());
    bool result = sysexec(cmd_str.c_str());
    assert(result); // ensure the cmd is executed successfully
                    // system(cmd_str.c_str());

    std::unique_ptr<Module> Mod(parseIRFile("top.bc", Err, Context));
    if (!Mod)
    {
        Err.print(argv[0], errs());
        return 1;
    }

    // Create a pass manager and fill it with the passes we want to run.
    legacy::PassManager PM;

    auto dominatortreewrapperpass = new DominatorTreeWrapperPass();
    PM.add(dominatortreewrapperpass);
    print_info("Enable DominatorTreeWrapperPass Pass");

    print_info("Enable HI_FindFunctions Pass");
    print_info("Enable HI_DependenceList Pass");
    auto hi_findfunction = new HI_FindFunctions();
    PM.add(hi_findfunction);
    auto hi_dependencelist = new HI_DependenceList("Instructions", "Instruction_Dep");
    PM.add(hi_dependencelist);

    print_info("Enable LoopSimplify Pass");

    print_info("Enable HI_LoopInFormationCollect Pass");
    auto hi_loopinformationcollect = new HI_LoopInFormationCollect("Loops");
    hi_loopinformationcollect->solveDependency(PM);
    PM.add(hi_loopinformationcollect);

    print_status("Start LLVM processing");
    PM.run(*Mod);
    print_status("Accomplished LLVM processing");

    print_status("Writing LLVM IR to File");
    std::error_code EC;
    llvm::raw_fd_ostream OS("top_output.bc", EC, llvm::sys::fs::F_None);
    WriteBitcodeToFile(*Mod, OS);
    OS.flush();

    print_status("Translate the IR to be readable one");
    cmd_str = "llvm-dis top_output.bc 2>&1";
    print_cmd(cmd_str.c_str());
    result = sysexec(cmd_str.c_str());
    assert(result); // ensure the cmd is executed successfully
    return 0;
}

    print_info("Enable IndVarSimplify Pass");
    auto loopsimplifypass = createLoopSimplifyPass();
    PM.add(createLoopSimplifyPass());


#include "LLVM_exp5_SimpleTimingAnalysis.h"

using namespace llvm;
using namespace polly;

int main(int argc, char **argv)
{
    if (argc < 3)
    {
        errs() << "Usage: " << argv[0] << " <C/C++ file> <Top_Function_Name>\n";
        return 1;
    }

    // Compile the source code into IR and Parse the input LLVM IR file into a module
    SMDiagnostic Err;
    LLVMContext Context;
    std::string cmd_str = "clang -O1 -emit-llvm -S " + std::string(argv[1]) + " -o top.bc 2>&1";
    std::string top_str = std::string(argv[2]);
    print_cmd(cmd_str.c_str());
    bool result = sysexec(cmd_str.c_str());
    assert(result); // ensure the cmd is executed successfully
                    // system(cmd_str.c_str());

    std::unique_ptr<Module> Mod(parseIRFile("top.bc", Err, Context));
    if (!Mod)
    {
        Err.print(argv[0], errs());
        return 1;
    }

    // Create a pass manager and fill it with the passes we want to run.
    legacy::PassManager PM;

    // LPPassManager *LPPM = new LPPassManager();
    // LPPM->add(new IndVarSimplifyPass());
    // PM.add(LPPM);

    print_info("Enable LoopSimplify Pass");
    auto loopsimplifypass = createLoopSimplifyPass();
    PM.add(loopsimplifypass);

    auto indvarsimplifypass = createIndVarSimplifyPass();
    PM.add(indvarsimplifypass);
    print_info("Enable IndVarSimplifyPass Pass");

    // auto loopstrengthreducepass = createLoopStrengthReducePass();
    // PM.add(loopstrengthreducepass);
    // print_info("Enable LoopStrengthReducePass Pass");

    auto loopinfowrapperpass = new LoopInfoWrapperPass();
    PM.add(loopinfowrapperpass);
    print_info("Enable LoopInfoWrapperPass Pass");

    auto regioninfopass = new RegionInfoPass();
    PM.add(regioninfopass);
    print_info("Enable RegionInfoPass Pass");

    auto scalarevolutionwrapperpass = new ScalarEvolutionWrapperPass();
    PM.add(scalarevolutionwrapperpass);
    print_info("Enable ScalarEvolutionWrapperPass Pass");

    auto loopaccesslegacyanalysis = new LoopAccessLegacyAnalysis();
    PM.add(loopaccesslegacyanalysis);
    print_info("Enable LoopAccessLegacyAnalysis Pass");

    auto dominatortreewrapperpass = new DominatorTreeWrapperPass();
    PM.add(dominatortreewrapperpass);
    print_info("Enable DominatorTreeWrapperPass Pass");

    auto optimizationremarkemitterwrapperpass = new OptimizationRemarkEmitterWrapperPass();
    PM.add(optimizationremarkemitterwrapperpass);
    print_info("Enable OptimizationRemarkEmitterWrapperPass Pass");

    auto hi_loopinformationcollect = new HI_LoopInFormationCollect("Loops");
    PM.add(hi_loopinformationcollect);
    print_info("Enable HI_LoopInFormationCollect Pass");

    auto aaresultswrapperpass = new AAResultsWrapperPass();
    print_info("Enable AAResultsWrapperPass Pass");
    PM.add(aaresultswrapperpass);

    auto scopdetectionwrapperpass = new ScopDetectionWrapperPass();
    print_info("Enable ScopDetectionWrapperPass Pass");
    PM.add(scopdetectionwrapperpass);

    auto assumptioncachetracker = new AssumptionCacheTracker();
    print_info("Enable AssumptionCacheTracker Pass");
    PM.add(assumptioncachetracker);

    auto scopinfowrapperpass = new ScopInfoWrapperPass();
    print_info("Enable ScopInfoWrapperPass Pass");
    PM.add(scopinfowrapperpass);

    auto scopinforegionpass = new ScopInfoRegionPass();
    print_info("Enable ScopInfoRegionPass Pass");
    PM.add(scopinforegionpass);

    auto dependenceinfowrapperpass = new DependenceInfoWrapperPass();
    print_info("Enable DependenceInfoWrapperPass Pass");
    PM.add(dependenceinfowrapperpass);

    auto polyhedralinfo = new PolyhedralInfo();
    print_info("Enable PolyhedralInfo Pass");
    PM.add(polyhedralinfo);

    auto hi_polly_info = new HI_Polly_Info("PollyInformation");
    print_info("Enable PollyInformation Pass");
    PM.add(hi_polly_info);

    // auto hi_loopdependenceanalysis = new HI_LoopDependenceAnalysis("HI_LoopDependenceAnalysis");
    // print_info("Enable HI_LoopDependenceAnalysis Pass");
    // PM.add(hi_loopdependenceanalysis);

    print_info("Enable HI_SimpleTimingEvaluation Pass");
    PM.add(hi_simpletimingevaluation);

    print_info("Enable HI_FindFunctions Pass");
    print_info("Enable HI_DependenceList Pass");
    auto hi_findfunction = new HI_FindFunctions();
    PM.add(hi_findfunction);
    auto hi_dependencelist = new HI_DependenceList("Instructions", "Instruction_Dep");
    PM.add(hi_dependencelist);
    // AU.addRequiredTransitive<polly::DependenceInfoWrapperPass>();
    // AU.addRequiredTransitive<polly::ScopInfoWrapperPass>();

    print_status("Start LLVM processing");
    PM.run(*Mod);
    print_status("Accomplished LLVM processing");

    assert(hi_simpletimingevaluation->topFunctionFound &&
           "The specified top function is not found in the program");

    print_status("Writing LLVM IR to File");
    std::error_code EC;
    llvm::raw_fd_ostream OS("top_output.bc", EC, llvm::sys::fs::F_None);
    WriteBitcodeToFile(*Mod, OS);
    OS.flush();

    print_status("Translate the IR to be readable one");
    cmd_str = "llvm-dis top_output.bc 2>&1";
    print_cmd(cmd_str.c_str());
    result = sysexec(cmd_str.c_str());
    assert(result); // ensure the cmd is executed successfully
    return 0;
}


typedef int flag;
typedef int int8;
typedef unsigned long long int bits64;
typedef unsigned long long float64;

float64 propagateFloat64NaN(float64 a, float64 b);


#include <stdint.h>

/*
Implementations based on:
Harish and Narayanan. "Accelerating large graph algorithms on the GPU using
CUDA." HiPC, 2007. Hong, Oguntebi, Olukotun. "Efficient Parallel Graph
Exploration on Multi-Core CPU and GPU." PACT, 2011.
*/

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Terminology (but not values) from graph500 spec
//   graph density = 2^-(2*SCALE - EDGE_FACTOR)
#define SCALE 8
#define EDGE_FACTOR 16

#define N_NODES (1LL << SCALE)
#define N_EDGES (N_NODES * EDGE_FACTOR)

// upper limit
#define N_LEVELS 10

// Larger than necessary for small graphs, but appropriate for large ones
typedef uint64_t edge_index_t;
typedef uint64_t node_index_t;

typedef struct edge_t_struct {
    // These fields are common in practice, but we elect not to use them.
    // weight_t weight;
    // node_index_t src;
    node_index_t dst;
} edge_t;

typedef struct node_t_struct {
    edge_index_t edge_begin;
    edge_index_t edge_end;
} node_t;

typedef int8_t level_t;
#define MAX_LEVEL INT8_MAX

void bfs(
    node_t nodes[N_NODES],
    edge_t edges[N_EDGES],


#include <stdint.h>

#include <stdio.h>
#include <stdlib.h>

#define FFT_SIZE 1024
#define twoPI 6.28318530717959

void fft(
    double real[FFT_SIZE],
    double img[FFT_SIZE],
    double real_twid[FFT_SIZE / 2],
    double img_twid[FFT_SIZE / 2]);


#include <stdint.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TYPE double

// Problem Constants
#define nAtoms 256
#define domainEdge 20.0
#define blockSide 4
// #define blockSide     1
#define nBlocks (blockSide * blockSide * blockSide)
#define blockEdge (domainEdge / ((TYPE)blockSide))
// Memory Bound
// This is an artifact of using statically-allocated arrays. We'll pretend that
// it doesn't exist and instead track the actual number of points.
#define densityFactor 10
// LJ coefficients
#define lj1 1.5
#define lj2 2.0

typedef struct {
    TYPE x, y, z;
} dvector_t;
typedef struct {
    int32_t x, y, z;
} ivector_t;

void md(
    int32_t n_points[blockSide][blockSide][blockSide],
    dvector_t force[blockSide][blockSide][blockSide][densityFactor],
    dvector_t position[blockSide][blockSide][blockSide][densityFactor]);


void kernel_jacobi_2d(double A[30][30], double B[30][30]);


#include "ap_int.h"

typedef ap_uint<1> bit1;
typedef ap_uint<8> bit8;

// struct: 2D triangle
typedef struct {
    bit8 x0;
    bit8 y0;
    bit8 x1;
    bit8 y1;
    bit8 x2;
    bit8 y2;
    bit8 z;
} Triangle_2D;

bit1 pixel_in_triangle(bit8 x, bit8 y, Triangle_2D triangle_2d);


    WT_TYPE weight[DIM_OUT][DIM_IN];
    WT_TYPE bias[DIM_OUT];
    FM_TYPE output[DIM_OUT];

    for (int i = 0; i < DIM_IN; i++) {
        input[i] = DIM_IN - 2 * FM_TYPE(i);
    }

    for (int i = 0; i < DIM_OUT; i++) {
        bias[i] = -WT_TYPE(i) / DIM_OUT;
        for (int j = 0; j < DIM_IN; j++) {
            weight[i][j] = WT_TYPE(i + j) / (DIM_OUT * DIM_IN);
        }
    }

    linear(input, weight, bias, output);

    for (int i = 0; i < DIM_OUT; i++) {
        printf("%f\n", output[i]);
    }

    return 0;
}


#include "nw_nw.h"

#define MATCH_SCORE 1
#define MISMATCH_SCORE -1
#define GAP_SCORE -1

#define ALIGN '\\'
#define SKIPA '^'
#define SKIPB '<'

#define MAX(A, B) (((A) > (B)) ? (A) : (B))

void needwun(
    char SEQA[ALEN],
    char SEQB[BLEN],
    char alignedA[ALEN + BLEN],
    char alignedB[ALEN + BLEN],
    int M[(ALEN + 1) * (BLEN + 1)],
    char ptr[(ALEN + 1) * (BLEN + 1)]) {

    int score, up_left, up, left, max;
    int row, row_up, r;
    int a_idx, b_idx;
    int a_str_idx, b_str_idx;

init_row:

        M[b_idx * (ALEN + 1)] = b_idx * GAP_SCORE;
    }

// Matrix filling loop
fill_out:
    for (b_idx = 1; b_idx < (BLEN + 1); b_idx++) {
    fill_in:
        for (a_idx = 1; a_idx < (ALEN + 1); a_idx++) {
            if (SEQA[a_idx - 1] == SEQB[b_idx - 1]) {
                score = MATCH_SCORE;
            } else {
                score = MISMATCH_SCORE;
            }

            row_up = (b_idx - 1) * (ALEN + 1);
            row = (b_idx) * (ALEN + 1);

            up_left = M[row_up + (a_idx - 1)] + score;
            up = M[row_up + (a_idx)] + GAP_SCORE;
            left = M[row + (a_idx - 1)] + GAP_SCORE;

            max = MAX(up_left, MAX(up, left));

            M[row + a_idx] = max;
            if (max == left) {
                ptr[row + a_idx] = SKIPB;
            } else if (max == up) {
                ptr[row + a_idx] = SKIPA;
            } else {
                ptr[row + a_idx] = ALIGN;
            }
        }
    }

    // TraceBack (n.b. aligned sequences are backwards to avoid string
    // appending)
    a_idx = ALEN;
    b_idx = BLEN;
    a_str_idx = 0;
    b_str_idx = 0;

trace:
    while (a_idx > 0 || b_idx > 0) {
        r = b_idx * (ALEN + 1);
        if (ptr[r + a_idx] == ALIGN) {
            alignedA[a_str_idx++] = SEQA[a_idx - 1];
            alignedB[b_str_idx++] = SEQB[b_idx - 1];
            a_idx--;
            b_idx--;
        } else if (ptr[r + a_idx] == SKIPB) {
            alignedA[a_str_idx++] = SEQA[a_idx - 1];
            alignedB[b_str_idx++] = '-';
            a_idx--;
        } else { // SKIPA
            alignedA[a_str_idx++] = '-';
            alignedB[b_str_idx++] = SEQB[b_idx - 1];
            b_idx--;
        }
    }

// Pad the result
pad_a:
    for (; a_str_idx < ALEN + BLEN; a_str_idx++) {
        alignedA[a_str_idx] = '_';
    }
pad_b:
    for (; b_str_idx < ALEN + BLEN; b_str_idx++) {
        alignedB[b_str_idx] = '_';
    }
}

    for (a_idx = 0; a_idx < (ALEN + 1); a_idx++) {
        M[a_idx] = a_idx * GAP_SCORE;
    }


/*
Implementation based on algorithm described in:
"Stencil computation optimization and auto-tuning on state-of-the-art multicore
architectures" K. Datta, M. Murphy, V. Volkov, S. Williams, J. Carter, L.
Oliker, D. Patterson, J. Shalf, K. Yelick SC 2008
*/

#include "stencil_stencil3d.h"

void stencil3d(TYPE C[2], TYPE orig[SIZE], TYPE sol[SIZE]) {
    int i, j, k;
    TYPE sum0, sum1, mul0, mul1;

// Handle boundary conditions by filling with original values
height_bound_col:
    for (j = 0; j < col_size; j++) {
    height_bound_row:
        for (k = 0; k < row_size; k++) {
            sol[INDX(row_size, col_size, k, j, 0)] =
                orig[INDX(row_size, col_size, k, j, 0)];
            sol[INDX(row_size, col_size, k, j, height_size - 1)] =
                orig[INDX(row_size, col_size, k, j, height_size - 1)];
        }
    }
col_bound_height:
    for (i = 1; i < height_size - 1; i++) {
    col_bound_row:
        for (k = 0; k < row_size; k++) {
            sol[INDX(row_size, col_size, k, 0, i)] =
                orig[INDX(row_size, col_size, k, 0, i)];
            sol[INDX(row_size, col_size, k, col_size - 1, i)] =
                orig[INDX(row_size, col_size, k, col_size - 1, i)];
        }
    }
row_bound_height:
    for (i = 1; i < height_size - 1; i++) {
    row_bound_col:

// Stencil computation
loop_height:
    for (i = 1; i < height_size - 1; i++) {
    loop_col:
        for (j = 1; j < col_size - 1; j++) {
        loop_row:
            for (k = 1; k < row_size - 1; k++) {
                sum0 = orig[INDX(row_size, col_size, k, j, i)];
                sum1 = orig[INDX(row_size, col_size, k, j, i + 1)] +
                       orig[INDX(row_size, col_size, k, j, i - 1)] +
                       orig[INDX(row_size, col_size, k, j + 1, i)] +
                       orig[INDX(row_size, col_size, k, j - 1, i)] +
                       orig[INDX(row_size, col_size, k + 1, j, i)] +
                       orig[INDX(row_size, col_size, k - 1, j, i)];
                mul0 = sum0 * C[0];
                mul1 = sum1 * C[1];
                sol[INDX(row_size, col_size, k, j, i)] = mul0 + mul1;
            }
        }
    }
}

        for (j = 1; j < col_size - 1; j++) {
            sol[INDX(row_size, col_size, 0, j, i)] =
                orig[INDX(row_size, col_size, 0, j, i)];
            sol[INDX(row_size, col_size, row_size - 1, j, i)] =


#include "heat-3d.h"

void kernel_heat_3d(

    double A[10][10][10],
    double B[10][10][10]) {
#pragma HLS top name = kernel_heat_3d

    const int n = 10;
    const int tsteps = 20;


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "trisolv.h"

void init_array(int n, double L[40][40], double x[40], double b[40]) {
    int i, j;

    for (i = 0; i < n; i++) {
        x[i] = -999;
        b[i] = i;
        for (j = 0; j <= i; j++)
            L[i][j] = (double)(i + n - j + 1) * 2 / n;
    }
}

void print_array(int n, double x[40])

{
    int i;

    fprintf(stderr, "==BEGIN DUMP_ARRAYS==\n");
    fprintf(stderr, "begin dump: %s", "x");
    for (i = 0; i < n; i++) {
        fprintf(stderr, "%0.6lf ", x[i]);
        if (i % 20 == 0)
            fprintf(stderr, "\n");
    }
    fprintf(stderr, "\nend   dump: %s\n", "x");
    fprintf(stderr, "==END   DUMP_ARRAYS==\n");
}

int main() {

    int n = 40;

    double L[40][40];
    double x[40];

    init_array(n, L, x, b);

    kernel_trisolv(L, x, b);

    print_array(n, x);

    return 0;
}


#include "clockwise_vertices.h"

void clockwise_vertices(Triangle_2D *triangle_2d) {

    tmp_x = triangle_2d->x0;
    tmp_y = triangle_2d->y0;

    triangle_2d->x0 = triangle_2d->x1;
    triangle_2d->y0 = triangle_2d->y1;

    triangle_2d->x1 = tmp_x;
    triangle_2d->y1 = tmp_y;
}


//===-- Utilities.h - Utilities Functions for Verilog Backend ---*- C++ -*-===//
//
//                      The Shang HLS frameowrk                               //
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements some utilities functions for Verilog Backend.
//
//===----------------------------------------------------------------------===//
#ifndef VTM_UTILITIES_H
#define VTM_UTILITIES_H
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Compiler.h"

namespace llvm {
inline unsigned getByteEnable(unsigned SizeInBytes) {
  return (0x1 << SizeInBytes) - 1;
}
// Get the bit slice in range (UB, LB].
/// GetBits - Retrieve bits between [LB, UB).
inline uint64_t getBitSlice64(uint64_t x, unsigned UB, unsigned LB = 0) {
  assert(UB - LB <= 64 && UB <= 64 && "Cannot extract bit slice!");
  // If the bit slice contains the whole 64-bit variable, simply return it.
  if (UB == 64 && LB == 0) return x;

  return (x >> LB) & ((uint64_t(1) << (UB - LB)) - 1);
}

/// SignExtend64 - Sign extend B-bit number x to 64-bit int.
/// Usage int64_t r = SignExtend64<5>(x);
inline int64_t SignExtend64(uint64_t x, unsigned SizeInBits) {
  return int64_t(x << (64 - SizeInBits)) >> (64 - SizeInBits);
}

inline bool isAllZeros64(uint64_t V, unsigned BitWidth) {
  return getBitSlice64(V, BitWidth) == getBitSlice64(UINT64_C(0), BitWidth);
}

inline bool isAllOnes64(uint64_t V, unsigned BitWidth) {
  return getBitSlice64(V, BitWidth) == getBitSlice64(~UINT64_C(0), BitWidth);

}

inline std::string VBEMangle(const std::string &S) {
  std::string Result;

  for (unsigned i = 0, e = S.size(); i != e; ++i)
    if (isalnum(S[i]) || S[i] == '_') {
      Result += S[i];
    } else {
      Result += '_';
      Result += 'A'+(S[i]&15);
      Result += 'A'+((S[i]>>4)&15);
      Result += '_';
    }
    return Result;
}

// PrintEscapedString - Print each character of the specified string, escaping
// it if it is not printable or if it is an escape char.
inline void PrintEscapedString(const char *Str, unsigned Length,
                               raw_ostream &Out) {
    for (unsigned i = 0; i != Length; ++i) {
      unsigned char C = Str[i];
      if (isprint(C) && C != '\\' && C != '"')
        Out << C;
      else if (C == '\\')
        Out << "\\\\";
      else if (C == '\"')
        Out << "\\\"";
      else if (C == '\t')
        Out << "\\t";
      else
        Out << "\\x" << hexdigit(C >> 4) << hexdigit(C & 0x0F);
    }
}

// PrintEscapedString - Print each character of the specified string, escaping
// it if it is not printable or if it is an escape char.
inline void PrintEscapedString(const std::string &Str, raw_ostream &Out) {
  PrintEscapedString(Str.c_str(), Str.size(), Out);
}

template <typename X, typename Y>

template <typename X, typename Y>
inline Y pair_second(std::pair<X, Y> P) { return P.second; }

class Module;
class TargetData;
class SMDiagnostic;
// Allow other pass to run script against the GlobalVariables.
bool runScriptOnGlobalVariables(Module &M, TargetData *TD,
                                const std::string &Script,
                                SMDiagnostic Err);
class MachineFunction;
class VASTModule;
void bindFunctionInfoToScriptEngine(MachineFunction &MF, TargetData &TD,
                                    VASTModule *Module);

class VASTModule;
// Bind VASTModule to script engine.
void bindToScriptEngine(const char *name, VASTModule *M);
bool runScriptFile(const std::string &ScriptPath, SMDiagnostic &Err);
bool runScriptStr(const std::string &ScriptStr, SMDiagnostic &Err);
//
unsigned getIntValueFromEngine(ArrayRef<const char*> Path);
std::string getStrValueFromEngine(ArrayRef<const char*> Path);

class MachineMemOperand;
class ScalarEvolution;
class SCEV;
// Alias Analysis.
AliasAnalysis::AliasResult
MachineMemOperandAlias(MachineMemOperand* V1, MachineMemOperand *V2,
                       AliasAnalysis *AA, ScalarEvolution *SE);

const SCEV *getMachineMemOperandSCEV(MachineMemOperand *V, ScalarEvolution *SE);

const SCEV *getAddressDeltaSCEV(MachineMemOperand *LHS, MachineMemOperand *RHS,
                                ScalarEvolution *SE);

int64_t getAddressDelta(MachineMemOperand *LHS, MachineMemOperand *RHS,
                        ScalarEvolution *SE);

static inline
bool isMachineMemOperandAlias(MachineMemOperand* V1, MachineMemOperand *V2,
                              AliasAnalysis *AA, ScalarEvolution *SE) {
  return MachineMemOperandAlias(V1, V2, AA, SE) != AliasAnalysis::NoAlias;
}

std::pair<const Value*, int64_t>
extractPointerAndOffset(const Value *V, int64_t Offset);

// Loop dependency Analysis.
int getLoopDepDist(bool SrcBeforeDest, int Distance = 0);

int getLoopDepDist(const SCEV *SSAddr, const SCEV *SDAddr,
                   bool SrcLoad, unsigned ElemSizeInByte, ScalarEvolution *SE);

class MachineBasicBlock;
class TargetInstrInfo;
void fixTerminators(MachineBasicBlock *MBB);
void fixTerminators(MachineFunction &MF);

class MachineDominatorTree;
class MachineInstr;
class MachineRegisterInfo;
bool hoistDatapathOp(MachineInstr *MI, MachineDominatorTree  *DT,
                     MachineRegisterInfo *MRI);

bool hoistDatapathOpInMBB(MachineBasicBlock *MBB, MachineDominatorTree *DT,
                          MachineRegisterInfo *MRI);

bool hoistDatapathOpInSuccs(MachineBasicBlock *MBB, MachineDominatorTree *DT,
                            MachineRegisterInfo *MRI);
}

#endif

inline X pair_first(std::pair<X, Y> P) { return P.first; }


/*
+--------------------------------------------------------------------------+
| CHStone : a suite of benchmark programs for C-based High-Level Synthesis |
| ======================================================================== |
|                                                                          |
| * Collected and Modified : Y. Hara, H. Tomiyama, S. Honda,               |
|                            H. Takada and K. Ishii                        |
|                            Nagoya University, Japan                      |
|                                                                          |
| * Remark :                                                               |
|    1. This source code is modified to unify the formats of the benchmark |
|       programs in CHStone.                                               |
|    2. Test vectors are added for CHStone.                                |
|    3. If "main_result" is 0 at the end of the program, the program is    |
|       correctly executed.                                                |
|    4. Please follow the copyright of each benchmark program.             |
+--------------------------------------------------------------------------+
*/
/*
 * Copyright (C) 2008
 * Y. Hara, H. Tomiyama, S. Honda, H. Takada and K. Ishii
 * Nagoya University, Japan
 * All rights reserved.
 *
 * Disclaimer of Warranty
 *
 * These software programs are available to the user without any license fee or
 * royalty on an "as is" basis. The authors disclaims any and all warranties,
 * whether express, implied, or statuary, including any implied warranties or
 * merchantability or of fitness for a particular purpose. In no event shall the
 * copyright-holder be liable for any incidental, punitive, or consequential damages
 * of any kind whatsoever arising from the use of these programs. This disclaimer
 * of warranty extends to the user of these programs and user's customers, employees,
 * agents, transferees, successors, and assigns.
 *
 */
const unsigned long imem[44] = {
  0x8fa40000,			// [0x00400000]  lw $4, 0($29)                   ; 175: lw $a0 0($sp)               # argc