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

Copyright (c) 2017 Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


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

#include "FlowGraph.h"
#include "BuildIR.h"

using namespace vISA;
// This is the debugging code.

// Type for ANode (Abstract Node)
typedef enum
{
    AN_UNUSED,

    // corresponding to each BB, not necessarily a hammock
    AN_BB,

    // The following are hammock nodes
    AN_HAMMOCK,   // temporarily used
    AN_IF_THEN_ENDIF,
    AN_IF_THEN_ELSE_ENDIF,
    AN_DO_WHILE,
    AN_COMPOSITE,
    AN_SEQUENCE
} ANodeType;

// Kind of ANode: indicate the final conversion choice of this ANode.
typedef enum {
    ANKIND_GOTOJOIN,     // goto/join
    ANKIND_JMPI,         // scalar jump (uniform branch)
    ANKIND_SCF           // structured CF
} ANodeKind;

namespace {

     enum ANodeBBType
    {
        ANODEBB_NORMAL,
        ANODEBB_CALL,
        ANODEBB_RETURN
    };

    class ControlGraph
    {
    public:
        // forward goto :  goto in beginBB, endBB is target of goto
        // backward goto:  goto in endBB, beginBB is target of goto
        G4_BB *beginBB;
        G4_BB *endBB;
        G4_INST *gotoInst;

        bool isBackward;
        bool isLoopCandidate;  // Used if isBackward is true

        ControlGraph(G4_BB *begin, G4_BB *end, G4_INST* inst, bool backward) :
            beginBB(begin), endBB(end), gotoInst(inst), isBackward(backward),
            isLoopCandidate(false)
        {}
    };

    // forward declaration
    class CFGStructurizer;
    class ANode;
    class ANodeBB;

    typedef std::vector<G4_BB*> BBVector;
    typedef std::vector<ANode*> ANVector;
    typedef std::list<ANode*> ANList;
    typedef std::vector<ControlGraph*> CGVector;
    typedef std::list<ControlGraph*> CGList;
    typedef std::map<G4_BB*, G4_BB*> BBToBBMap;
    typedef std::map<G4_BB*, ANodeBB*> BBToANodeBBMap;
    typedef std::map<ANode*, ANode*> ANodeMap;

    // ANode base, abstract class
    class ANode
    {
    public:
        uint32_t  anodeId;
        ANodeType type;
        ANList    preds;
        ANList    succs;
        ANode     *parent;
        ANodeKind kind;


        // Indicate if this node is in ACFG. ACFG changes as BB is
        // processed, and once a group of nodes are condensed and
        // represented by a new, single node, the new node will be
        // in ACFG and all condensed nodes are no longer in ACFG.
        bool      inACFG;

        // True if structured CF is allowed for this node (for example,
        // do_while ANode has break inst that we cannot handle for now.
        // Then, set this field to false so we has to use goto/join.)
        bool      allowSCF;

        // True if it has break. Note that break isn't represented by
        // a separate ANode type.  So, if AN_BB has this field set to
        // true, its goto is a break. For other ANode, it means it
        // contains some AN_BB that has break.
        bool      hasBreak;

        bool      visited;

        virtual G4_BB* getBeginBB() const = 0;
        virtual G4_BB* getEndBB() const = 0;
        virtual G4_BB* getExitBB() const = 0;
        virtual void setBeginBB(G4_BB *bb) = 0;
        virtual void setEndBB(G4_BB *bb) = 0;
        virtual void setExitBB(G4_BB *bb) = 0;

        explicit ANode() :
            type(AN_UNUSED), preds(), succs(),  parent(nullptr),
            kind(ANKIND_GOTOJOIN), inACFG(true), allowSCF(true),
            hasBreak(false), visited(false) {}
        explicit ANode(ANodeType Ty) :
            type(Ty), preds(), succs(),  parent(nullptr),
            kind(ANKIND_GOTOJOIN), inACFG(true), allowSCF(true),
            hasBreak(false), visited(false) {}
        virtual ~ANode() {}

        ANodeType getType() const { return type; }
        void setType(ANodeType t) { type = t; }
        ANodeKind getKind() const { return kind; }
        void setKind(ANodeKind t) { kind = t; }

        bool isInACFG() const { return inACFG; }
        void setInACFG(bool v) { inACFG = v; }

        // Return true if this node's type is a SCF type.
        bool isSCF() const;
        bool isHammock() const;

        ANode* getCurrentANode() const;
        ANode* getChildANode(ANode *parent);
        bool isMember(ANode *nd) const;
        bool isExitPhySucc() const;
        bool getHasBreak() const { return hasBreak; }
        void setHasBreak(bool v) { hasBreak = v; }
        bool getAllowSCF() const { return allowSCF; }
        void setAllowSCF(bool v) { allowSCF = v; }
        bool isVisited() const { return visited; }
        void setVisited(bool v) {visited = v; }

    private:
        // copy-ctor and assignment operator are not allowed
        ANode(const ANode&);
        ANode& operator= (const ANode&);
    };

    // ANodeBB : ANode for a single BB
    class ANodeBB : public ANode
    {
    public:
        G4_BB *bb;
        ANodeBBType type;
        // Used during convertPST() to tell if a BB's label has been used as jmpi's target. As we
        // convert PST from the outer to inner. If this is true, endif/join etc (to this label) must
        // need a new BB (new label).
        // For example:
        //       goto label0                    jmpi label0
        //       ...                            ...
        //       (p) goto label0   --->         if (goto)
        //       ...                            ...
        //     label0:                        label_new:
        //                                      endif (join)
        //                                    label0:
        //
        bool isJmpiTarget;

        explicit ANodeBB() : ANode(AN_BB), bb(nullptr), type(ANODEBB_NORMAL), isJmpiTarget(false) {}
        explicit ANodeBB(G4_BB *b) :
            ANode(AN_BB), bb(b), type(ANODEBB_NORMAL) {}
        virtual ~ANodeBB() {}

        void setANodeBBType(ANodeBBType v) { type = v; }

        // ExitBB is the target BB for BB with goto inst or
        // next BB otherwise. If no successor, return nullptr.
        virtual G4_BB* getExitBB() const
        {
            if (type == ANODEBB_CALL)
            {
                return bb->getPhysicalSucc();
            }
            return (type == ANODEBB_RETURN || bb->Succs.size() == 0)
                ? nullptr : bb->Succs.back();
        }
        virtual G4_BB* getBeginBB() const { return bb; }
        virtual G4_BB* getEndBB()   const { return bb; }

        virtual void setExitBB(G4_BB *b)  { MUST_BE_TRUE(false, "setExitBB() not allowed for ANodeBB"); }
        virtual void setBeginBB(G4_BB *b) { MUST_BE_TRUE(false, "setBeginBB() not allowed for ANodeBB"); }
        virtual void setEndBB(G4_BB *b)   { MUST_BE_TRUE(false, "setEndBB() not allowed for ANodeBB"); }
    };

    // Hammock Graph
    class ANodeHG : public ANode
    {
    public:
        ANList children; // in order, 1st one is entryNode.

        // This node has BBs from beginBB to endBB, inclusive; and
        // every outgoing edges will go to exitBB. exitBB is not
        // part of this node.
        G4_BB* beginBB;
        G4_BB* endBB;
        G4_BB* exitBB;   // not part of this ANode
        ANode* exitNode; // not used yet

        /* Temporary variables used during ANodeHG constructions */
        bool isLoopCandidate;

        // Index to ANStack (vector) when this node is on ANStack.
        // -1 means it is not on ANStack.
        int32_t ANStackIx;

        explicit ANodeHG(ANodeType t) :
            ANode(t), beginBB(nullptr), endBB(nullptr), exitBB(nullptr),
            exitNode(nullptr), isLoopCandidate(false), ANStackIx(-1)
        { }
        explicit ANodeHG(ControlGraph *cg, ANodeBB *nodebb);
        virtual ~ANodeHG() {}

        ANode* getEntryNode() const { return children.size() > 0 ? children.front() : nullptr; }
        ANode* getEndNode() const { return children.size() > 0 ? children.back() : nullptr; }
        ANode* getExitNode(ANodeBB *exitANBB);
        void   setExitNode(ANode *nd) { exitNode = nd; }

        virtual G4_BB* getBeginBB() const { return beginBB; }
        virtual G4_BB* getEndBB() const { return endBB; }
        virtual G4_BB* getExitBB() const { return exitBB; }
        virtual void setBeginBB(G4_BB* b) { beginBB = b; }
        virtual void setEndBB(G4_BB *b) { endBB = b; }
        virtual void setExitBB(G4_BB* b) { exitBB = b; }
    };

    // CFG Structurizer
    //
    // [TODO] algo description
    //
    // For a CFG that has gotos, convert it to structurized control flow
    // so that we can use structured control-flow instructions, jmpi,
    // and goto/join to replace them.
    //
    // This algorithm uses the existing order of BBs and will not reorder
    // them.
    class CFGStructurizer
    {
    private:
        FlowGraph  *CFG;
        uint32_t   numOfBBs;

        // Used to assign unique id to each ANode
        uint32_t numOfANodes;

        // For new BB, map its ID to its insertion BB,
        // ie, the original BB after which it is inserted.
        BBToBBMap  newBBToInsertAfterBB;

        // Given a BB, find its ANodeBB.
        // Access them via setANodeBB()/getANodeBB().
        struct {
            ANodeBB *IDToANodeBB;
            BBToANodeBBMap newBBToANodeBB;
        } anodeBBs;

        // caching the flags
        bool doScalarJmp;
        bool doStructCF;

        // Assume that the entire CFG is the single ANode: Root ANode for
        // short (We don't actually build it, but imagine we have one). A root
        // ANodes are the ANodes whose parent is this Root ANode and who
        // are also hammock. In another word, any BB with goto should belong
        // to one of topANodes(only one of them, not more than 1).
        ANVector topANodes;

        // Used during constructing hammock ANodes.
        ANVector  ANStack;

        // BBs is the list of BBs that shows the layout order. Originally,
        // it is the same as the input CFG->BBs. As the algo goes, new BBs
        // can be inserted in the list.
        BB_LIST* BBs;               // ptr to CFG->BBs
        G4_ExecSize kernelExecSize;     // default execSize

        void init();
        void fini();
        void deletePST(ANodeHG* node);
        void preProcess();

        // return true if bb is the start of ControlGraph.
        bool getCGBegin(G4_BB *bb, CGList &cgs);
        void insertAtBegin(G4_BB* aBB, G4_INST *anInst);

        // Insert 'cg' in the ordered list 'cgs'
        void cg_oinsert(CGList &cgs, ControlGraph *cg);
        void bb_oinsert(BB_LIST &bblist, G4_BB *bb);
        bool isGotoScalarJmp(G4_INST *gotoInst);
        bool isJoinBB(G4_BB* bb);
        bool canbeJoinBB(G4_BB *bb);
        bool isBBLabelAvailable(G4_BB *bb);
        ANode *ANListGetOther(ANList &anlist, ANode *node);
        void reConnectAllPreds(ANode *from, ANode *to);
        void reConnectAllSuccs(ANode *from, ANode *to);

        // Use ANStack_push/ANStack_pop to push/pop ANStack
        void ANStack_push(ANodeHG* AN)
        {
            AN->ANStackIx = (uint32_t) ANStack.size();
            ANStack.push_back(AN);
        }

        ANodeHG *ANStack_pop()
        {
            ANodeHG *AN = (ANodeHG *)ANStack.back();
            ANStack.pop_back();
            AN->ANStackIx = (uint32_t)(-1);
            return AN;
        }

        uint32_t getNextANodeId() { return numOfANodes++; }
        void constructPST(BB_LIST_ITER IB, BB_LIST_ITER IE);
        void reConstructDoWhileforBreak(ANodeHG *whileNode);
        void ANStack_reconstruct(ANodeHG *& node, ANodeBB *ndbb);
        ANodeHG *finalizeHG(ANodeHG *node);
        void condenseNode(ANodeHG *node);

        // Return true if bb0 appears before bb1, false otherwise.
        // (If bb0 is the same as bb1, return false.)
        bool isBefore(G4_BB *bb0, G4_BB *bb1);

        ANodeBB *getANodeBB(G4_BB *b);
        void setANodeBB(ANodeBB *a, G4_BB *b);
        G4_BB *createBBWithLabel();
        G4_BB *getInsertAfterBB(G4_BB *bb);
        void setInsertAfterBB(G4_BB *newbb, G4_BB *insertAfter);
        G4_BB* findInsertAfterBB(G4_BB *bb);
        void adjustBBId(G4_BB *newbb);

        void getNewRange(G4_BB*& end, G4_BB*& exit, G4_BB* end1, G4_BB* exit1);
        void extendNode(ANode *Node, ControlGraph *CG);
        ANodeHG* getEnclosingANodeHG(ANode *AN);
        ANode* mergeWithPred(ANode *AN);
        ANodeBB* addLandingBB(
            ANodeHG *node, BB_LIST_ITER insertAfterIter, bool updateACFG);
        ANode *addSplitBBAtBegin(G4_BB *splitBB);
        ANode* addSplitBBAtEnd(G4_BB *splitBB);
        bool isANodeOnlyPred(G4_BB *abb, ANode *node);
        void PSTAddANode(
            ANodeHG *parent, ANode *node, ANode *newNode, bool isAfter);

        bool convertPST(ANode *node, G4_BB *nextJoinBB);
        void setJoinJIP(G4_BB* joinBB, G4_BB* jipBB);
        void convertChildren(ANodeHG *nodehg, G4_BB *nextJoinBB);
        void convertIf(ANodeHG *node, G4_BB *nextJoinBB);
        void convertDoWhile(ANodeHG *node, G4_BB *nextJoinBB);
        void convertGoto(ANodeBB *node, G4_BB *nextJoinBB);
        void generateGotoJoin(G4_BB *gotoBB, G4_BB *jibBB, G4_BB *targetBB);

        // helper
        // return goto inst if any; null otherwise
        G4_INST *getGotoInst(G4_BB *bb);
        G4_INST *getJmpiInst(G4_BB *bb);

    private:
        // Don't implement
        CFGStructurizer(const CFGStructurizer&);
        CFGStructurizer& operator=(const CFGStructurizer&);

    public:

        static void ANListReplace(ANList &anlist, ANode *from, ANode *to);
        static void BBListReplace(BB_LIST &bblist, G4_BB *from, G4_BB *to);
        static BB_LIST_ITER findBB(BB_LIST *bblist, G4_BB *bb);
        static ANList::iterator findANode(ANList &anlist, ANode *nd);
        static void ANListErase(ANList &anlist, ANode *toBeErased);
        static void BBListErase(BB_LIST &bblist, G4_BB *toBeErased);

        CFGStructurizer(FlowGraph *cfg) : CFG(cfg)
        {
            init();
        }

        ~CFGStructurizer()
        {
            fini();
        }

        void run();

#if     0
        // Forward definition
        class DebugSelector;
        DebugSelector *dbgSel;
#endif

#ifdef _DEBUG
        void doIndent(uint32_t numOfSpaces); // Generate indent
        void dump();                         // Dump the program(kernel/shader)
        void dumpcfg();                      // Dump the CFG only without instructions
        void dump(G4_BB *bb);                // Dump the given bb
        void dump(uint32_t BBId);            // Dump BB with the given BBId
        void dump(char *labelString);        // Dump BB with the given labelString
        void dump(ANode *node, uint32_t indentSpaces);  // Dump node recursively
        void dumpacfg();                     // Dump ACFG.
#endif
    };
}  // namespace

#ifdef _DEBUG

// 0:  default, dumps only CF related instructions (CF instr, label)
// 1:  All instructions
static int dump_level = 0;
static const char* currFileName = nullptr;
static std::ofstream dump_ofs;
static std::ostream* dumpOut = &std::cout;  // default

// use this func or debugger to set dump_level
void setDumpLevel(int l) { dump_level = l;}

// Direct output to a given file or std::cout
void resetoutput() { dumpOut = & std::cout; }  // restore default
void setoutputfile(const char* filename)
{
    if (dump_ofs.is_open()) dump_ofs.close();

    currFileName = filename;
    dump_ofs.open(filename,
              std::ofstream::out | std::ofstream::ate | std::ofstream::trunc);
    dump_ofs.flush();
    dumpOut = &dump_ofs;
}
void forceFlush()
{
    if (dump_ofs.is_open())
    {
        dump_ofs.close();
        dump_ofs.open(currFileName,
            std::ofstream::out | std::ofstream::app);
    }
}

//
// CFG dump routines
//
void dumpbbbase(G4_BB *bb)
{
    if (dump_level == 0)
    {
        for (INST_LIST_ITER it = bb->begin(), ie = bb->end();
            it != ie; ++it)
        {
            G4_INST* inst = *it;
            if (!inst->isLabel() && !inst->isFlowControl())
            {
                continue;
            }
            bb->emitInstruction(*dumpOut, it);
        }
    }
    else
    {
        bb->emit(*dumpOut);
    }
    dumpOut->flush();
}

void dumpbb(G4_BB *bb)
{
    (*dumpOut) << "\n[BB" << bb->getId() << "] ";
    dumpbbbase(bb);
}

void dumpbbid(BB_LIST *bblist, uint32_t BBId)
{
    for (BB_LIST_ITER I = bblist->begin(), E = bblist->end();
        I != E; ++I)
    {
        G4_BB *bb = *I;
        if (bb->getId() == BBId)
        {
            dumpbb(bb);
            break;
        }
    }
}

void dumpbblabel(BB_LIST *bblist, char* labelString)
{
    for (BB_LIST_ITER I = bblist->begin(), E = bblist->end();
        I != E; ++I)
    {
        G4_BB *bb = *I;
        G4_Label *labelInst = bb->getLabel();
        if (labelInst && strcmp(labelInst->getLabel(), labelString) == 0)
        {
            dumpbb(bb);
            break;
        }
    }
}

void dumpallbbs(BB_LIST *bblist)
{
    (*dumpOut) << "Program Dump\n";

    for (BB_LIST_ITER I = bblist->begin(), E = bblist->end();
        I != E; ++I)
    {
        G4_BB *bb = *I;
        dumpbb(bb);
    }
    dumpOut->flush();
}

void dumpbblist(BB_LIST* bblist)
{
    (*dumpOut) << "\nCFG dump\n\n";

    for (BB_LIST_ITER I = bblist->begin(), E = bblist->end();
        I != E; ++I)
    {
        G4_BB *bb = *I;
        G4_Label *labelInst = bb->getLabel();
        (*dumpOut) << "  BB(" << bb->getId() << ")";
        if (labelInst)
        {
            (*dumpOut) << " " << labelInst->getLabel() << ",";
        }
        (*dumpOut) << "  Preds:";
        for (BB_LIST_ITER I = bb->Preds.begin(), E = bb->Preds.end();
            I != E; ++I)
        {
            G4_BB *pred = *I;
            (*dumpOut) << " " << pred->getId();
        }
        (*dumpOut) << "    Succs:";
        for (BB_LIST_ITER I = bb->Succs.begin(), E = bb->Succs.end();
            I != E; ++I)
        {
            G4_BB *succ = *I;
            (*dumpOut) << " " << succ->getId();
        }
        (*dumpOut) << "\n";
    }
    dumpOut->flush();
}


// CFGStructurizer::dump(), etc cannot be invoked in MSVC's immediate window.
// Use file-scope functions with cfgs prefix as work-around.
void cfgsdump(CFGStructurizer *p)
{
    p->dump();
}

void cfgsdumpcfg(CFGStructurizer *p)
{
    p->dumpcfg();
}

void cfgsdump(CFGStructurizer *p, uint32_t BBId)
{
    p->dump(BBId);
}

void cfgsdump(CFGStructurizer *p, G4_BB *bb)
{
    p->dump(bb);
}

void cfgsdump(CFGStructurizer *p, char *labelName)
{
    p->dump(labelName);
}

void cfgsdump(CFGStructurizer *p, ANode *nd)
{
    p->dump(nd, 0);
}

void cfgsdumpacfg(CFGStructurizer *p)
{
    p->dumpacfg();
}

// Debugging functions
const char *getANodeTypeString(ANodeType t)
{
    switch (t)
    {
    case AN_BB: return "BB";
    case AN_HAMMOCK: return "HAMMOCK";
    case AN_IF_THEN_ENDIF: return "IF_THEN_ENDIF";
    case AN_IF_THEN_ELSE_ENDIF: return "IF_THEN_ELSE_ENDIF";
    case AN_DO_WHILE: return "DO_WHILE";
    case AN_COMPOSITE: return "COMPOSITE";
    case AN_SEQUENCE: return "SEQUENCE";
    default:
        return " ";
    }
}

const char *getANodeKindString(ANodeKind t)
{
    switch (t)
    {
    case ANKIND_GOTOJOIN: return "gotojoin";
    case ANKIND_JMPI: return "jmpi";
    case ANKIND_SCF: return "structuredCF";
    }
    return " ";
}

void CFGStructurizer::doIndent(uint32_t numOfSpaces)
{
    for (uint32_t ix = 0; ix < numOfSpaces; ++ix)
    {
        (*dumpOut) << " ";
    }
}

void CFGStructurizer::dump(ANode *node, uint32_t level)
{
    // Just show the single digit nesting level
    uint32_t showLevel = level % 10;
    uint32_t indentSpaces = level * 4;

    ANodeType Ty = node->getType();
    doIndent(indentSpaces);
    (*dumpOut) << "  "
              << showLevel
              << "-node("
              << node->anodeId << ") ["
              << getANodeTypeString(Ty)
              << "]  begin=BB" << node->getBeginBB()->getId()
              << "  end=BB" << node->getEndBB()->getId();
    G4_BB *exitbb = node->getExitBB();
    if (exitbb)
    {
        (*dumpOut) << "  exit=BB" << exitbb->getId();
    }
    else
    {
        (*dumpOut) << "  exit=NULL";
    }
    (*dumpOut) << "\n";
    doIndent(indentSpaces);
    (*dumpOut) << "    Attr:";
    if (node->getType() == AN_BB && getGotoInst(node->getEndBB()))
    {
        (*dumpOut) << " " << getANodeKindString(node->getKind());
    }
    if (node->getHasBreak())
    {
        (*dumpOut) << " hasBreak";
    }
    if (node->isSCF())
    {
        (*dumpOut) << (node->getAllowSCF() ? " allowSCF" : " notAllowSCF");
    }
    (*dumpOut) << "\n";

    doIndent(indentSpaces);
    (*dumpOut) << "    preds:";
    for (ANList::iterator I = node->preds.begin(), E = node->preds.end();
        I != E; ++I)
    {
        ANode *nd = *I;
        (*dumpOut) << " " << nd->anodeId;
    }
    (*dumpOut) << "  succs:";
    for (ANList::iterator I = node->succs.begin(), E = node->succs.end();
        I != E; ++I)
    {
        ANode *nd = *I;
        (*dumpOut) << " " << nd->anodeId;
    }
    (*dumpOut) << "\n";
    doIndent(indentSpaces);
    if (node->parent)
    {
        (*dumpOut) << "    parent: " << node->parent->anodeId << "\n";
    }
    else
    {
        (*dumpOut) << "    parent:\n";
    }
    if (node->isHammock())
    {
        ANodeHG *ndhg = (ANodeHG*)node;
        doIndent(indentSpaces);
        (*dumpOut) << "    children:\n";
        for (ANList::iterator I = ndhg->children.begin(), E = ndhg->children.end();
            I != E; ++I)
        {
            ANode *nd = *I;
            dump(nd, level + 1);
        }
    }
    dumpOut->flush();
}

void CFGStructurizer::dump(G4_BB *bb)
{
    dumpbbbase(bb);
}

void CFGStructurizer::dump(uint32_t BBId)
{
    dumpbbid(BBs, BBId);
}

void CFGStructurizer::dump(char* labelString)
{
    dumpbblabel(BBs, labelString);
}

void CFGStructurizer::dump()
{
    (*dumpOut) << "Program Dump\n\n";
    dumpallbbs(BBs);
}

void CFGStructurizer::dumpcfg()
{
    dumpbblist(BBs);
}

void CFGStructurizer::dumpacfg()
{
    (*dumpOut) << "\nACFG dump\n\n";

    BB_LIST_ITER I = BBs->begin();
    BB_LIST_ITER E = BBs->end();
    while (I != E)
    {
        BB_LIST_ITER nextI = I;
        ++nextI;
        G4_BB *bb = *I;
        ANodeBB *ndbb = getANodeBB(bb);
        if (ndbb)
        {
            ANode *nd = ndbb->getCurrentANode();
            dump(nd, 0);
            G4_BB *endbb = nd->getEndBB();
            G4_BB *phyNext = endbb->getPhysicalSucc();
            nextI = findBB(BBs, phyNext);
        }
        I = nextI;
    }
    dumpOut->flush();
}

#endif


inline bool ANode::isSCF() const
{
    return (type == AN_IF_THEN_ENDIF ||
            type == AN_IF_THEN_ELSE_ENDIF ||
            type == AN_DO_WHILE);
}

inline bool ANode::isHammock() const
{
    return (type == AN_IF_THEN_ENDIF ||
            type == AN_IF_THEN_ELSE_ENDIF ||
            type == AN_DO_WHILE ||
            type == AN_COMPOSITE ||
            type == AN_SEQUENCE);
}

inline ANode* ANode::getCurrentANode() const
{
    const ANode *nd = this;
    while (nd && !nd->isInACFG())
    {
        nd = nd->parent;
    }
    MUST_BE_TRUE(nd && nd->isInACFG(),
                 "ACFG flag must be set incorrectly");
    return const_cast<ANode*>(nd);
}

inline bool ANode::isMember(ANode *nd) const
{
    while (nd && nd != this)
    {
        nd = nd->parent;
    }
    return nd == this;
}

inline bool ANode::isExitPhySucc() const
{
    const G4_BB *endbb = getEndBB();
    const G4_BB *exitbb = getExitBB();
    return exitbb && endbb && endbb->getPhysicalSucc() == exitbb;
}

ANodeHG::ANodeHG(ControlGraph *cg, ANodeBB* nodebb)
    : ANode(AN_HAMMOCK), children()
{
    if (nodebb)
    {
        children.push_back(nodebb);
        nodebb->parent = this;
    }
    exitNode = nullptr;
    isLoopCandidate = false;
    ANStackIx = -1;

    // AN_HAMMOCK is temporary. Its succs/preds won't be availabe until
    // it is converted to a final hammock node, ie, finalized. So, it
    // is actually not in ACFG yet.

    bool isCondGoto = (cg->gotoInst->getPredicate() != NULL);
    setBeginBB(cg->beginBB);

    if (cg->isBackward)
    {
        // set up loop
        setEndBB(cg->endBB);
        G4_BB*  succ0 = cg->endBB->Succs.front();
        G4_BB *bb = isCondGoto ? succ0 : nullptr;
        setExitBB(bb);
    }
    else
    {
        setEndBB(cg->beginBB);
        setExitBB(cg->endBB);
    }
}

ANode* ANodeHG::getExitNode(ANodeBB *exitANBB)
{
#if 0
    if (exitNode)
    {
        return exitNode;
    }
#endif

    ANode *nd = exitANBB;
    if (nd)
    {
        nd = nd->getCurrentANode();
    }
    return nd;
}

ANodeHG *getInnerMostWhile(ANode *node)
{
    ANode *tmp = node;
    while (tmp && tmp->getType() != AN_DO_WHILE)
    {
        tmp = tmp->parent;
    }
    return (ANodeHG *)tmp;
}

// If bb is inside this ANode, return the top node (direct child
// node), otherwise, return nullptr.
ANode *ANode::getChildANode(ANode *parent)
{
    ANode *nd = this;
    while (nd && nd->parent != parent)
    {
        nd = nd->parent;
    }
    return nd;
}

//  Preprocess CFG so that the structurizer can be simpler without considering
/// those corner cases. Currently, it does:
//  1) avoid sharing target label among forward gotos and backward gotos
//     For example,
//          goto L
//       L:
//          goto L
//
//     changed to
//           goto L0
//       L0:      (new empty BB)
//       L1:
//           goto L1
//  2) avoid a fall-thru BB of a backward goto BB is the target BB of another
//     backward goto (two back-to-back loops). For example,
//          L0:
//            (p0) goto L0
//          L1 :
//            (p1) goto L1
//    changed to
//          L0:
//            (p0) goto L0
//          L :         (new empty BB)
//          L1 :
//            (p1) goto L1
// 3) make sure the last BB's pred is its physical predecessor.
//    In another word, if there is a case:
//          B0 : goto L1
//          ...
//        L0:
//          ...
//          B1:  goto L0
//        L1:
//           ret
//   changed to
//          B0 : goto L
//          ...
//       L0:
//          ...
//          B1: goto L0
//       L:            (new empty BB)
//       L1:
//          ret
//   This case is to guarantee that the last HG has its valid, non-null exit BB
//   except Basic block ANode. (Without this, the last HG isn't handled completely
//   with the current algo.)
void CFGStructurizer::preProcess()
{
    bool CFGChanged = false;
    for (BB_LIST_ITER BI = CFG->begin(), BE = CFG->end();
        BI != BE; ++BI)
    {
        G4_BB* B = *BI;
        if (B->getBBType() & (G4_BB_RETURN_TYPE | G4_BB_INIT_TYPE))
        {
            // If B is RETURN_BB, don't insert as all cases should not happen,
            // so is INIT BB.
            continue;
        }

        bool insertEmptyBBBefore = false;
        // Both entry's end (with or without subroutine and subroutine's end.
        G4_BB* phySucc = B->getPhysicalSucc();
        bool isLastBB = ((std::next(BI) == BE)
            || (B->Succs.empty() && phySucc && (phySucc->getBBType() & G4_BB_INIT_TYPE))
            || (B->getBBType() & G4_BB_EXIT_TYPE));
        BB_LIST_ITER IT, IE;
        if (isLastBB)
        {
            // case 3
            if (B->Preds.size() >= 2)
            {
                // Maybe don't set if B's physical pred is its pred.
                // But doing so shouldn't cause any perf regression!
                insertEmptyBBBefore = true;
            }
            else if (B->Preds.size() == 1)
            {
                G4_BB* phyPred = B->getPhysicalPred();
                G4_BB* pred = B->Preds.back();
                insertEmptyBBBefore = (phyPred != pred);
            }
        }

        if (!insertEmptyBBBefore && B->Preds.size() >= 2)
        {
            // case 1 & 2
            // Check if this B is a successor of both backward and forward branches.
            bool isForwardTarget = false;
            bool isBackwardTarget = false;
            bool isFallThruOfBackwardGotoBB = false;
            for (IT = B->Preds.begin(), IE = B->Preds.end(); IT != IE; IT++)
            {
                G4_BB* P = *IT;
                G4_INST* gotoInstP = getGotoInst(P);
                if (!gotoInstP)
                {
                    continue;
                }
                if (P->getId() >= B->getId())
                {
                    isBackwardTarget = true;
                    continue;
                }
                // P is a BB before B
                if (P->getPhysicalSucc() != B)
                {
                    isForwardTarget = true;
                }
                else if (P->getPhysicalSucc() == B
                    && gotoInstP->asCFInst()->isBackward())
                {
                    isFallThruOfBackwardGotoBB = true;
                }
            }

            if (   (isBackwardTarget && isForwardTarget)              // case 1
                || (isBackwardTarget && isFallThruOfBackwardGotoBB))  // case 2
            {
                insertEmptyBBBefore = true;
            }
        }

        if (!insertEmptyBBBefore)
        {
            continue;
        }

        // Now, create an empty BB right before "B", and adjust all forward
        // branching to this new BB and leave all backward branching unchanged.
        G4_BB *newBB = createBBWithLabel();
        G4_Label *newLabel = newBB->getLabel();

        // Adjust BB's pred/succs
        BB_LIST_ITER NextIT;
        IT = B->Preds.begin();
        IE = B->Preds.end();
        for (NextIT = IT; IT != IE; IT = NextIT)
        {
            ++NextIT;
            G4_BB* P = *IT;
            if (P->getId() >= B->getId())
            {
                // keep the backward branch unchanged.
                continue;
            }
            G4_INST* gotoInst = getGotoInst(P);
            G4_INST* jmpiInst = getJmpiInst(P);
            if (   !gotoInst && !jmpiInst       // not jump to B
                && P->getPhysicalSucc() != B)   // not fall-thru to B
            {
                // Shouldn't happen.
                assert(false && "unknown control-flow instruction!");
            }

            // forward branching/fall-thru "P->B" is changed to "P->newBB"
            BBListReplace(P->Succs, B, newBB);
            newBB->Preds.push_back(P);

            // Change this branching
            if (gotoInst && gotoInst->asCFInst()->getUip() == B->getLabel())
            {
                gotoInst->asCFInst()->setUip(newLabel);
            }
            else if (jmpiInst && jmpiInst->getSrc(0) == B->getLabel())
            {
                jmpiInst->setSrc(newLabel, 0);
            }

            // the edge from B's Preds
            B->Preds.erase(IT);
        }
        newBB->Succs.push_back(B);
        B->Preds.push_front(newBB);

        // insert it into BBs
        CFG->insert(BI, newBB);
        G4_BB *phyPred = B->getPhysicalPred();
        assert(phyPred && "B should have physical pred!");
        phyPred->setPhysicalSucc(newBB);
        newBB->setPhysicalPred(phyPred);
        newBB->setPhysicalSucc(B);
        B->setPhysicalPred(newBB);

        CFGChanged = true;
    }

    if (CFGChanged)
    {
        // New BBs generated, reassign block Ids.
        CFG->reassignBlockIDs();
    }
}

void CFGStructurizer::init()
{
#if _DEBUG
    // Default dump level.
    // To change dump level while debugging, using setDumpLevel().
    dump_level = 0;
#endif

    // Assume G4_BB's id is set from 0 to numOfBBs - 1. This id
    // is used to map G4_BB to ANodeBB.
    // (Note that removeUnreachableBlocks() right before invoking this
    //  pass will reassign block IDs.)

    // First, preprocess CFG so that the structurizer does not need to handle corner cases.
    // Currently, it does:
    //    1. no forward gotos and backward gotos share the same label (but allow that
    //       all forward gotos can share the same label, so can all backward gotos);
    //    2. No fall-thru BB of a backward goto BB is also the target BB of another
    //       backward goto.
    preProcess();

    BBs = &(CFG->getBBList());
    numOfBBs = CFG->getNumBB();
    numOfANodes = 0;
    kernelExecSize = CFG->getKernel()->getSimdSize();

    // caching the flags
    doScalarJmp = !CFG->builder->noScalarJmp();
    doStructCF = CFG->builder->getOption(vISA_StructurizerCF);

    if (numOfBBs == 0)
    {
        anodeBBs.IDToANodeBB = nullptr;
        return;
    }

    anodeBBs.IDToANodeBB = new ANodeBB [numOfBBs];

    // CFG at this moment has subroutine calls explicitly linked (between call and
    // callee and callee's return to call's succ). For structurizing purpose, those
    // edges should not be considered. Here ANodeBB will have those edges ignored.
    uint32_t i = 0;
    for (BB_LIST_ITER II = CFG->begin(), IE = CFG->end(); II != IE; ++II, ++i)
    {
        G4_BB *bb = *II;
        uint32_t id = bb->getId();
        ANodeBB *node = &(anodeBBs.IDToANodeBB[id]);
        node->anodeId = getNextANodeId();
        node->bb = bb;
        bool isCallBB = (bb->isEndWithCall() || bb->isEndWithFCall());
        bool isReturnBB = (bb->getLastOpcode() == G4_return || bb->isEndWithFRet());
        if (isCallBB)
        {
            G4_BB *returnBB = bb->getPhysicalSucc();
            assert(returnBB && "Call BB must have a return BB.");
            ANode *succNode = &(anodeBBs.IDToANodeBB[returnBB->getId()]);
            node->succs.push_back(succNode);
            succNode->preds.push_back(node);

            node->setANodeBBType(ANODEBB_CALL);
        }
        else if (isReturnBB)
        {
            node->setANodeBBType(ANODEBB_RETURN);
        }
        else
        {
            for (BB_LIST_ITER I = bb->Succs.begin(), E = bb->Succs.end(); I != E; ++I)
            {
                G4_BB *succ = *I;
                ANode *succNode = &(anodeBBs.IDToANodeBB[succ->getId()]);
                node->succs.push_back(succNode);
                succNode->preds.push_back(node);
            }
        }
    }
}

ANodeBB* CFGStructurizer::getANodeBB(G4_BB *bb)
{
    uint32_t id = bb->getId();
    if (id < numOfBBs)
    {
        return &(anodeBBs.IDToANodeBB[id]);
    }

    BBToANodeBBMap::iterator I = anodeBBs.newBBToANodeBB.find(bb);
    MUST_BE_TRUE(I != anodeBBs.newBBToANodeBB.end(),
                 "Corresponding ANodeBB isn't set up yet");
    ANodeBB *ndbb = I->second;
    return ndbb;
}

void CFGStructurizer::setANodeBB(ANodeBB *ndbb, G4_BB *bb)
{
    uint32_t id = bb->getId();
    if (id < numOfBBs)
    {
        MUST_BE_TRUE(false, "ANodeBB has been set up already");
        return;
    }
    MUST_BE_TRUE(anodeBBs.newBBToANodeBB.find(bb) == anodeBBs.newBBToANodeBB.end(),
                 "ANodeBB has been in map already");
    anodeBBs.newBBToANodeBB[bb] = ndbb;
}

G4_BB *CFGStructurizer::getInsertAfterBB(G4_BB *bb)
{
    MUST_BE_TRUE(newBBToInsertAfterBB.find(bb) != newBBToInsertAfterBB.end(),
                 "The BB isn't a new BB or something else is wrong");
    MUST_BE_TRUE(bb->getId() >= numOfBBs, "The BB isn't a new BB");
    return newBBToInsertAfterBB[bb];
}

void CFGStructurizer::setInsertAfterBB(G4_BB *newbb, G4_BB *insertAfter)
{
    MUST_BE_TRUE(newbb->getId() >= numOfBBs, "The BB isn't a new BB");

    if (insertAfter->getId() < numOfBBs)
    {
        newBBToInsertAfterBB[newbb] = insertAfter;
    }
    else
    {
        newBBToInsertAfterBB[newbb] = getInsertAfterBB(insertAfter);
    }
}

void CFGStructurizer::deletePST(ANodeHG *nodehg)
{
    for (ANList::iterator I = nodehg->children.begin(),
        E = nodehg->children.end();
        I != E; ++I)
    {
        ANode *tmp = *I;
        if (tmp->getType() != AN_BB)
        {
            deletePST((ANodeHG*)tmp);
        }
    }
    nodehg->children.clear();
    delete nodehg;
}

// Return goto inst if bb has one, null otherwise.
G4_INST* CFGStructurizer::getGotoInst(G4_BB *bb)
{
    G4_INST *inst = bb->size() > 0 ? bb->back() : nullptr;
    return (inst && inst->opcode() == G4_goto) ? inst : nullptr;
}

// Return either jmpi inst if bb has one, null otherwise.
G4_INST* CFGStructurizer::getJmpiInst(G4_BB *bb)
{
    G4_INST *inst = bb->size() > 0 ? bb->back() : nullptr;
    return (inst && inst->opcode() == G4_jmpi) ? inst : nullptr;
}

void CFGStructurizer::fini()
{
    for (uint32_t i = 0, size = topANodes.size(); i< size; ++i)
    {
        deletePST((ANodeHG*)topANodes[i]);
    }
    topANodes.clear();

    if (numOfBBs > 0)
    {
        delete[] anodeBBs.IDToANodeBB;
        anodeBBs.IDToANodeBB = nullptr;

        for (BBToANodeBBMap::iterator I = anodeBBs.newBBToANodeBB.begin(),
                                      E = anodeBBs.newBBToANodeBB.end();
            I != E; ++I)
        {
            ANodeBB *tmp = I->second;
            delete tmp;
        }
        anodeBBs.newBBToANodeBB.clear();

        newBBToInsertAfterBB.clear();
    }
}

ANode *CFGStructurizer::ANListGetOther(ANList &anlist, ANode *node)
{
    if (anlist.size() == 0)
    {
        return nullptr;
    }
    ANode *nd = anlist.back();
    if (nd == node)
    {
        nd = anlist.front();
    }
    return (nd == node) ? nullptr : nd;
}

// Insert cg into the ordered CGList in the increasing order
// of cg's endBB.
void CFGStructurizer::cg_oinsert(CGList &cgs, ControlGraph *cg)
{
    bool hasInserted = false;
    CGList::iterator I = cgs.begin();
    CGList::iterator E = cgs.end();
    G4_BB *end = cg->endBB;
    while (I != E)
    {
        CGList::iterator Iter = I;
        ++I;
        ControlGraph *tmp = *Iter;
        if (isBefore(tmp->endBB, end))
        {
            continue;
        }
        cgs.insert(Iter, cg);
        hasInserted = true;
        break;
    }
    if (!hasInserted)
    {
        cgs.push_back(cg);
    }
}

// Insert BB into the ordered BB_List in the increasing order of BB
void CFGStructurizer::bb_oinsert(BB_LIST &bblist, G4_BB *bb)
{
    bool hasInserted = false;
    BB_LIST_ITER I = bblist.begin();
    BB_LIST_ITER E = bblist.end();
    while (I != E)
    {
        BB_LIST_ITER Iter = I;
        ++I;
        G4_BB *tmp = *Iter;
        if (isBefore(tmp, bb))
        {
            continue;
        }
        bblist.insert(Iter, bb);
        hasInserted = true;
        break;
    }
    if (!hasInserted)
    {
        bblist.push_back(bb);
    }
}

// Created all CGs whose beginBB is bb. If any, return true and
// all CGs are returned as cgs. If not, return false.
//
// cgs is in order so that the last one will be outmost block
// and the first one is the innermost block. If bb has goto
// inst, the CG for this goto will be the first one in cgs.
bool CFGStructurizer::getCGBegin(G4_BB *bb, CGList &cgs)
{
    if (bb->size() == 0)
    {
        return false;
    }

    // If bb's goto is backward to a BB prior to bb, this bb cannot
    // be a loop candidate.  For example,
    //
    //       B0:
    //          ......
    //       bb: ..
    //         (p) goto B0
    //       ...
    //       B1:
    //         (p) goto bb
    //  In this case, CG for bb is marked with non loop candidate.
    //
    G4_INST *bbgotoInst = getGotoInst(bb);
    bool isLoopCandidate = true;
    if (bbgotoInst && bbgotoInst->asCFInst()->isBackward() &&
        bb->Succs.size() > 0 && bb->Succs.back() != bb)
    {
        isLoopCandidate = false;
    }

    ControlGraph *cg;
    bool isBegin = false;
    // Check if bb is a loop head
    for (BB_LIST_ITER I = bb->Preds.begin(), E = bb->Preds.end(); I != E; ++I)
    {
        G4_BB *pred = *I;
        G4_INST *gotoInst = getGotoInst(pred);
        if (gotoInst && gotoInst->asCFInst()->isBackward() && pred->Succs.back() == bb)
        {
            cg = new ControlGraph(bb, pred, gotoInst, true);
            cg_oinsert(cgs, cg);
            cg->isLoopCandidate = isLoopCandidate;
            isBegin = true;
        }
    }

    // check if its last inst is a forward goto. Note that all backward
    // gotos have been processed when the target BB of this backward goto
    // is processed.
    G4_INST *gotoInst = getGotoInst(bb);
    if (gotoInst && !gotoInst->asCFInst()->isBackward())
    {
        G4_BB *succ = bb->Succs.back();
        cg = new ControlGraph(bb, succ, gotoInst, false);
        // Add this cg into the beginning of cgs.
        cgs.push_front(cg);
        isBegin = true;
    }
    return isBegin;
}

inline bool CFGStructurizer::isGotoScalarJmp(G4_INST *gotoInst)
{
    MUST_BE_TRUE(gotoInst->opcode() == G4_goto, "It should be a goto inst");
    return gotoInst->getExecSize() == g4::SIMD1 ||
           gotoInst->getPredicate() == nullptr ||
           gotoInst->asCFInst()->isUniform();
}

inline bool CFGStructurizer::isJoinBB(G4_BB* bb)
{
    G4_INST* joinInst = bb->getFirstInst();
    return joinInst && (joinInst->opcode() == G4_join);
}

inline bool CFGStructurizer::canbeJoinBB(G4_BB *bb)
{
    // We treat while/else/endif as the first inst
    // of BB. Whenever a BB has any of them, join
    // cannot be inserted as the first inst.
    G4_INST* firstInst = bb->getFirstInst();
    if (!firstInst)
    {
        // Emptry BB can always have a join
        return true;
    }
    G4_opcode opc = firstInst->opcode();
    return opc != G4_while && opc != G4_endif && opc != G4_else;
}

// return true if bb's label has not been used by while/else/endif/join
bool CFGStructurizer::isBBLabelAvailable(G4_BB *bb)
{
    G4_INST* firstInst = bb->getFirstInst();
    if (!firstInst)
    {
        return true;
    }
    G4_opcode opc = firstInst->opcode();
    return opc != G4_while && opc != G4_endif &&
           opc != G4_else && opc != G4_join;
}

void CFGStructurizer::ANListReplace(ANList &anlist, ANode *from, ANode *to)
{
    for (ANList::iterator I = anlist.begin(), E = anlist.end(); I != E; ++I)
    {
        ANode *tmp = *I;
        if (tmp == from)
        {
            (*I) = to;
            return;
        }
    }
}

void CFGStructurizer::BBListReplace(BB_LIST &bblist, G4_BB *from, G4_BB *to)
{
    for (BB_LIST_ITER I = bblist.begin(), E = bblist.end(); I != E; ++I)
    {
        G4_BB *tmp = *I;
        if (tmp == from)
        {
            (*I) = to;
            return;
        }
    }
    MUST_BE_TRUE(false, "BBList should have to-be-replaced element");
}

void CFGStructurizer::ANListErase(ANList &anlist, ANode *toBeErased)
{
    ANList::iterator I = findANode(anlist, toBeErased);
    if (I != anlist.end())
    {
        anlist.erase(I);
    }
}

void CFGStructurizer::BBListErase(BB_LIST &bblist, G4_BB *toBeErased)
{
    BB_LIST_ITER I = findBB(&bblist, toBeErased);
    if (I != bblist.end())
    {
        bblist.erase(I);
    }
}

// Given ranges [end, exit] and [end1, exit1], and
//      end < exit && end1 < exit1
// return the last two BBs of them. Note that the order is the
// BB order as given in CFG's BBs list.
//
// Note that it's possible that exit could be nullptr, but end
// will never be nullptr. And under some cases, return exit is
// unknown (set to nullptr).
void CFGStructurizer::getNewRange(
    G4_BB*& end, G4_BB*& exit, G4_BB* end1, G4_BB* exit1)
{
    G4_BB *e0, *e1;
    G4_BB* newEnd = isBefore(end, end1) ? end1 : end;

    if (exit && exit1)
    {
        if (exit == exit1)
        {
            // exit is still the new exit
            end = newEnd;
            return;
        }

        if (isBefore(exit, exit1))
        {
            e0 = exit1;
            e1 = exit;
        }
        else
        {
            e0 = exit;
            e1 = exit1;
        }

        if (isBefore(newEnd, e1))
        {
            newEnd = e1;
        }
        exit = e0;
        end = newEnd;
        return;
    }

    e0 = exit ? exit : exit1;
    if (e0 && (e0 == newEnd || isBefore(e0, newEnd)))
    {
        e0 = nullptr;
    }

    exit = e0;
    end = newEnd;
}

// Given a cg, if it definitely does not start a HG, no new HG should
// be created.  Instead, the range covered by this cg will be used to
// extend the current Node's range. This function is used to extend
// Node's range from this cg.
void CFGStructurizer::extendNode(ANode *Node, ControlGraph *CG)
{
    if (!Node)
    {
        return;
    }
    // Extending end & exit
    G4_BB *begin = CG->beginBB;
    G4_BB* end = CG->endBB;
    G4_BB* exit;
    if (CG->isBackward)
    {
        G4_BB*  succ0 = end->Succs.front();
        bool isCondGoto = (CG->gotoInst->getPredicate() != NULL);
        exit = isCondGoto ? succ0 : nullptr;
    }
    else
    {
        exit = end;
        end = begin;
    }

    getNewRange(end, exit, Node->getEndBB(), Node->getExitBB());
    Node->setEndBB(end);
    Node->setExitBB(exit);
}


// Get the immediate enclosing ANodeHG that contains all the preds of AN
// and all AN's backward succs (due to backward gotos).
ANodeHG* CFGStructurizer::getEnclosingANodeHG(ANode *AN)
{
    MUST_BE_TRUE(!ANStack.empty(), "ANStack should not be empty");

    G4_BB *begin = AN->getBeginBB();
    int32_t ix = (int32_t) ANStack.size() - 1;
    if (ANStack.size() > 1)
    {
        // As loop's end and begin must be in the same HG. Here, check
        // if AN has a backward goto, if so, merge ANode of the loop's
        // begin with AN.  However, it is possible that the ANode for
        // the loop's begin is not in ANStack anymore (due to previous
        // merging); and in this case, find ANode that contains loop's
        // begin and merge with AN.
        if (AN->getType() == AN_BB)
        {
            G4_INST *gotoInst = getGotoInst(begin);
            if (gotoInst && gotoInst->asCFInst()->isBackward())
            {
                uint32_t j;
                ANodeHG *hg = nullptr;
                for (j = (uint32_t) ANStack.size(); j != 0; --j)
                {
                    ANodeHG *tmp = (ANodeHG*)ANStack[j - 1];
                    if (tmp->isLoopCandidate && tmp->endBB == begin)
                    {
                        // ANode for innermost loop
                        hg = tmp;
                        break;
                    }
                }
                if (j == 0)
                {
                    // loop's begin ANode is no longer on ANStack. Find ANStack's node
                    // that enclose the loop's begin.
                    G4_BB *loopBeginBB = begin->Succs.back();
                    if (loopBeginBB != begin)
                    {
                        hg = (ANodeHG *)(getANodeBB(loopBeginBB)->getCurrentANode());
                        // The parent of ACFG node should be on ANStack
                        hg = (ANodeHG *)hg->parent;
                    }
                }

                if (hg)
                {
                    MUST_BE_TRUE(hg->ANStackIx >= 0, "ANodeHG's index is wrong");
                    if (ix > hg->ANStackIx)
                    {
                        ix = hg->ANStackIx;
                    }
                }
            }
        }

        for (ANList::iterator II = AN->preds.begin(), IE = AN->preds.end();
            II != IE; ++II)
        {
            ANode *pred = *II;
            if (pred == AN || isBefore(begin, pred->getBeginBB()))
            {
                // Skip. For pred == AN, they are already in AN;
                // for backward goto, its pred will be processed later.
                continue;
            }

            ANodeHG *hg = (ANodeHG *)pred->parent;
            if (hg)
            {
                MUST_BE_TRUE(hg->ANStackIx >= 0, "ANodeHG's index is wrong");
                if (ix > hg->ANStackIx)
                {
                    ix = hg->ANStackIx;
                }
            }
            else
            {
                // Error message
                G4_BB* predBB = pred->getBeginBB();
                G4_INST* predGoto = getGotoInst(predBB);

                // If predGoto is null, it means that a non-goto branching inst like jmpi jumps
                // into a range of goto instrutions, such as the following:
                //     jmp  A
                //     goto B
                //  A:
                //     ...
                //  B:
                //
                MUST_BE_TRUE(predGoto != nullptr, "Error: Non-goto (like jmp) and goto crossing !");
                MUST_BE_TRUE(false, "Error: unknown control flow in the program.");
            }
        }
        // No need to check succs as forward goto will be handled later
        // and backward goto has been handled already!
    }

    return (ANodeHG *)ANStack[ix];
}

// Merge AN with its immediate pred if possible.
//   1. If pred is AN_SEQUENCE, just add AN into pred
//   2. otherwise, create AN_SEQUENCE and include both
// Return the new merged node if merged, or AN otherwise.
ANode *CFGStructurizer::mergeWithPred(ANode *AN)
{
    if (AN->parent == nullptr ||
        AN->succs.size() != 1 || AN->preds.size() != 1)
    {
        return AN;
    }

    // check if it can be merged with the previous ANode. Note that we
    // only need to check its immediate predecessor under the same parent
    // as it is the only candidate for possible merging.
    ANode *node = AN;
    ANodeHG *parent = (ANodeHG *)AN->parent;
    ANode *pred = AN->preds.back();
    if (pred->succs.size() != 1 || (ANodeHG*)pred->parent != parent)
    {
        return AN;
    }

    // Get AN's physical previous ANode.
    ANList::iterator E = parent->children.end();
    ANList::iterator I = findANode(parent->children, pred);
    MUST_BE_TRUE(I != E, "Child Node isn't in Parent's children list");
    ++I;
    ANode* predPhySucc = (I != E) ? *I : nullptr;

    ANode *succ = AN->succs.back();
    if (predPhySucc == AN)
    {
        // check:  do we allow self-looping on SEQ node ?
        if (pred->getType() == AN_SEQUENCE)
        {
            ANodeHG *predhg = (ANodeHG *)pred;
            ANode *lastNode = predhg->children.back();
            AN->parent = predhg;
            predhg->children.push_back(AN);
            predhg->succs.clear();
            predhg->succs.push_back(succ);
            ANListReplace(succ->preds, AN, predhg);

            lastNode->succs.clear();
            lastNode->succs.push_back(AN);
            AN->preds.clear();
            AN->preds.push_back(lastNode);

            predhg->setEndBB(AN->getEndBB());
            predhg->setExitBB(AN->getExitBB());
            if (succ != pred)
            {
                predhg->setExitNode(succ);
            }

            ANListErase(parent->children, AN);
            AN->setInACFG(false);
            node = predhg;
        }
        else
        {
            ANodeHG *newSeqNode = new ANodeHG(AN_SEQUENCE);
            newSeqNode->anodeId = getNextANodeId();
            newSeqNode->parent = parent;
            pred->parent = newSeqNode;
            AN->parent = newSeqNode;
            ANList::iterator II = pred->preds.begin();
            ANList::iterator IE = pred->preds.end();
            while (II != IE)
            {
                ANList::iterator Iter = II;
                ++II;
                ANode *tmp = *Iter;
                if (tmp == AN || AN->isMember(tmp))
                {
                    newSeqNode->succs.push_back(newSeqNode);
                    newSeqNode->preds.push_back(newSeqNode);
                }
                else
                {
                    ANListReplace(tmp->succs, pred, newSeqNode);
                    newSeqNode->preds.push_back(tmp);
                }
            }

            // The above loop has handled case of succ == pred already
            if (succ != pred)
            {
                ANListReplace(succ->preds, AN, newSeqNode);
                newSeqNode->succs.push_back(succ);
            }
            newSeqNode->children.push_back(pred);
            newSeqNode->children.push_back(AN);
            pred->setInACFG(false);
            AN->setInACFG(false);

            newSeqNode->setBeginBB(pred->getBeginBB());
            newSeqNode->setEndBB(AN->getEndBB());
            newSeqNode->setExitBB(AN->getExitBB());

            if (succ != pred)
            {
                newSeqNode->setExitNode(succ);
            }

            ANListErase(parent->children, pred);
            ANListErase(parent->children, AN);
            parent->children.push_back(newSeqNode);

            node = newSeqNode;
        }
    }
    return node;
}

// Condensing a HG into a single node in ACFG. The children of node is still
// a valid partial ACFG. The current ACFG is the all ANode whose inACFG is true.
void CFGStructurizer::condenseNode(ANodeHG *node)
{
    ANode *entryNode = node->getEntryNode();
    G4_BB *exit = node->getExitBB();
    ANList::iterator I = entryNode->preds.begin();
    ANList::iterator E = entryNode->preds.end();
    while (I != E)
    {
        ANList::iterator Iter = I;
        ++I;
        ANode *pred = *Iter;
        if (node->isMember(pred))
        {
            continue;
        }

        node->preds.push_back(pred);
        ANListReplace(pred->succs, entryNode, node);
    }

    if (exit) {
        ANode *exitNode = node->getExitNode(getANodeBB(exit));
        I = exitNode->preds.begin();
        E = exitNode->preds.end();
        while (I != E)
        {
            ANList::iterator iter = I;
            ++I;
            ANode *pred = *iter;
            if (node->isMember(pred))
            {
                exitNode->preds.erase(iter);
            }
        }
        exitNode->preds.push_back(node);
        node->succs.push_back(exitNode);
    }

    // Set children nodes' inAFCG flag to false.
    E = node->children.end();
    for (I = node->children.begin(); I != E; ++I)
    {
        ANode* nd = *I;
        nd->setInACFG(false);
    }
}

ANodeHG *CFGStructurizer::finalizeHG(ANodeHG *node)
{
    // Identify the type of this node
    ANode *entryNode = node->getEntryNode();
    ANode *endNode = node->getEndNode();
    G4_BB *exitBB = node->getExitBB();
    ANode *exitNode = nullptr;
    if (exitBB) {
        exitNode = node->getExitNode(getANodeBB(exitBB));
        if (exitNode)
        {
            // Set exitNode when node is finalized
            node->setExitNode(exitNode);
        }
    }
    G4_BB *begin = node->getBeginBB();
    G4_INST *gotoInst = begin->back();
    if (endNode->succs.size() > 0 && endNode->succs.back() == entryNode)
    {
        node->setType(AN_DO_WHILE);
    }
    else if (entryNode->getType() == AN_BB && gotoInst &&
             gotoInst->getPredicate() && !gotoInst->asCFInst()->isBackward())
    {
        ANodeType ty = AN_COMPOSITE;
        if (entryNode->succs.size() == 2)
        {
            ANode *thenNode = entryNode->succs.front();
            ANode *elseNode = entryNode->succs.back();
            if (thenNode == elseNode)
            {
                // The CFG is not well formed and has the following
                //     BB0: succs=BB1, BB1
                // Simply make it COMPOSITE as an work-around. Will
                // fix igc to not generate such a case.
                ty = AN_COMPOSITE;
            }
            else if (thenNode->succs.size() == 1 && thenNode->succs.front() == elseNode &&
                elseNode == exitNode)
            {
                ty = AN_IF_THEN_ENDIF;
            }
            else if (thenNode->succs.size() == 1 && elseNode->succs.size() == 1 &&
                thenNode->succs.front() == elseNode->succs.front() &&
                thenNode->succs.front() == exitNode)
            {
                ty = AN_IF_THEN_ELSE_ENDIF;
            }
        }
        node->setType(ty);
    }
    else
    {
        node->setType(AN_COMPOSITE);
    }

    condenseNode(node);

    if (node->getType() == AN_DO_WHILE)
    {
        reConstructDoWhileforBreak(node);
    }

    return node;
}

BB_LIST_ITER CFGStructurizer::findBB(BB_LIST* bblist, G4_BB *bb)
{
    for (BB_LIST_ITER I = bblist->begin(), E = bblist->end(); I != E; ++I)
    {
        G4_BB *tmp = *I;
        if (tmp == bb)
        {
            return I;
        }
    }
    return bblist->end();
}

ANList::iterator CFGStructurizer::findANode(ANList& anlist, ANode *nd)
{
    for (ANList::iterator I = anlist.begin(), E = anlist.end(); I != E; ++I)
    {
        ANode *tmp = *I;
        if (tmp == nd)
        {
            return I;
        }
    }
    return anlist.end();
}

// Return true if all preds of abb is inside node; false otherwise.
bool CFGStructurizer::isANodeOnlyPred(G4_BB *abb, ANode *node)
{
    for (BB_LIST_ITER II = abb->Preds.begin(), IE = abb->Preds.end();
        II != IE; ++II)
    {
        G4_BB *tmp = *II;
        ANodeBB *ndbb = getANodeBB(tmp);

        // Note that isMember() requires PST is up to date.
        if (!node->isMember(ndbb))
        {
            return false;
        }
    }
    return true;
}

//
//  This function creates a new BB as node's exit BB. Node must be
//  finalized before invoking this function. InsertAfterIter is the
//  iterator to node->getEndBB(), which is provided if the calling
//  context already has it; otherwise, this function will compute
//  this iterator.
//
//  If updateACFG is true, ACFG will be updated. This is used during
//  the construction of ACFG and the new BB will always in ACFG
//  (as ANodeBB).  Note that PST is up to its caller to update.
//
ANodeBB* CFGStructurizer::addLandingBB(
    ANodeHG *node, BB_LIST_ITER insertAfterIter, bool updateACFG)
{
    G4_BB *endbb = node->getEndBB();
    if (insertAfterIter == BBs->end())
    {
        insertAfterIter = findBB(BBs, endbb);
    }
    G4_BB *insertAfterBB = *insertAfterIter;
    G4_BB *exitBB = node->getExitBB();

    G4_BB *newBB = createBBWithLabel();
    G4_Label *newLabel = newBB->getLabel();
    G4_Label *targetLabel = exitBB->getLabel();
    setInsertAfterBB(newBB, insertAfterBB);

    // Adjust BB's pred/succs
    BB_LIST_ITER BE = exitBB->Preds.end();
    BB_LIST_ITER BI = exitBB->Preds.begin();
    while (BI != BE)
    {
        BB_LIST_ITER curr = BI;
        ++BI;
        G4_BB *bb = *curr;
        ANode *tmp = getANodeBB(bb);
        if (node->isMember(tmp))
        {
            BBListErase(exitBB->Preds, bb);
            BBListReplace(bb->Succs, exitBB, newBB);
            newBB->Preds.push_back(bb);
            G4_INST *gotoInst = getGotoInst(bb);
            if (gotoInst)
            {
                if (gotoInst->asCFInst()->getUip() == targetLabel)
                {
                    if (bb == insertAfterBB)
                    {
                        // remove goto
                        bb->pop_back();
                    }
                    else
                    {
                        // modify the goto to branch to the new target
                        gotoInst->asCFInst()->setUip(newLabel);
                    }
                }
            }
            else
            {
                G4_INST *jmpiInst = getJmpiInst(bb);
                if (jmpiInst)
                {
                    if (jmpiInst->getSrc(0) == targetLabel)
                    {
                        if (bb == insertAfterBB)
                        {
                            // remove jmpi
                            bb->pop_back();
                        }
                        else
                        {
                            // modify the jmpi to branch to the new target
                            jmpiInst->setSrc(newLabel, 0);
                        }
                    }
                }
                else if (bb != insertAfterBB)
                {
                    MUST_BE_TRUE(false, "BB has unknown non-goto/jmpi branch");
                }
            }

            // Update exit nodes
            tmp = tmp->parent;
            while (tmp != node)
            {
                if (tmp->getExitBB() == exitBB)
                {
                    tmp->setExitBB(newBB);
                }
                tmp = tmp->parent;
            }
        }
    }

    if (insertAfterBB->getPhysicalSucc() != exitBB)
    {
        G4_INST *gotoInst = CFG->builder->createInternalCFInst(
                NULL, G4_goto, g4::SIMD1, NULL, targetLabel, InstOpt_NoOpt);
        newBB->push_back(gotoInst);
    }
    newBB->Succs.push_back(exitBB);
    exitBB->Preds.push_back(newBB);

    // insert it into BBs
    ++insertAfterIter;
    BBs->insert(insertAfterIter, newBB);
    G4_BB *phySucc = insertAfterBB->getPhysicalSucc();
    insertAfterBB->setPhysicalSucc(newBB);
    newBB->setPhysicalPred(insertAfterBB);
    newBB->setPhysicalSucc(phySucc);
    if (phySucc)
    {
        phySucc->setPhysicalPred(newBB);
    }

    ANodeBB *ndbb = new ANodeBB(newBB);
    ndbb->anodeId = getNextANodeId();
    setANodeBB(ndbb, newBB);

    if (updateACFG)
    {
        // Node has been finalized. Its children's ACFG is no longer
        // up to date. But within this node, acfg is still good;
        // only edges outside of this node is no longer valid!
        // We will rely on begin/end/exit when doing conversion.
        ANode *exitNode = getANodeBB(exitBB)->getCurrentANode();
        ANListReplace(exitNode->preds, node, ndbb);
        ANListReplace(node->succs, exitNode, ndbb);
        ndbb->succs.push_back(exitNode);
        ndbb->preds.push_back(node);
    }

    node->setExitBB(newBB);
    adjustBBId(newBB);

    return ndbb;
}

// Add a new empty BB as a predecessor of splitBB.
ANode *CFGStructurizer::addSplitBBAtBegin(G4_BB *splitBB)
{
    G4_BB *newBB = createBBWithLabel();
    G4_Label *newLabel = newBB->getLabel();
    G4_BB *insertAfter = findInsertAfterBB(splitBB->getPhysicalPred());
    setInsertAfterBB(newBB, insertAfter);

    // Adjust BB's pred/succs
    newBB->Preds.splice(newBB->Preds.end(), splitBB->Preds);
    splitBB->Preds.clear();
    splitBB->Preds.push_back(newBB);
    newBB->Succs.push_back(splitBB);
    BB_LIST_ITER BE = newBB->Preds.end();
    BB_LIST_ITER BI = newBB->Preds.begin();
    while (BI != BE)
    {
        BB_LIST_ITER curr = BI;
        ++BI;
        G4_BB *pred = *curr;
        BBListReplace(pred->Succs, splitBB, newBB);
        G4_INST *gotoInst = getGotoInst(pred);
        if (gotoInst)
        {
            if (gotoInst->asCFInst()->getUip() == splitBB->getLabel())
            {
                // modify the goto to branch to the new target
                gotoInst->asCFInst()->setUip(newLabel);
            }
        }

        // adjust exit BB
        ANode *nd = getANodeBB(pred)->parent;
        while (nd)
        {
            if (nd->getExitBB() == splitBB)
            {
                nd->setExitBB(newBB);
            }
            nd = nd->parent;
        }
    }

    // insert it into BBs
    BB_LIST_ITER iter = findBB(BBs, splitBB);
    BBs->insert(iter, newBB);
    G4_BB *phyPred = splitBB->getPhysicalPred();
    phyPred->setPhysicalSucc(newBB);
    newBB->setPhysicalPred(phyPred);
    newBB->setPhysicalSucc(splitBB);
    splitBB->setPhysicalPred(newBB);

    ANodeBB *ndbb = new ANodeBB(newBB);
    ndbb->anodeId = getNextANodeId();
    setANodeBB(ndbb, newBB);

    // newBB's id is the largest at this moment. Make sure it is in order
    adjustBBId(newBB);
    return ndbb;
}

// Add a new BB that only holds the branch instruction of splitBB
// if it has one, otherwise, the new BB is empty.
ANode* CFGStructurizer::addSplitBBAtEnd(G4_BB *splitBB)
{
    G4_BB *newBB = createBBWithLabel();
    setInsertAfterBB(newBB, splitBB);

    // Adjust splitBB's pred/succs
    BB_LIST_ITER BE = splitBB->Succs.end();
    BB_LIST_ITER BI = splitBB->Succs.begin();
    for (; BI != BE; ++BI)
    {
        G4_BB *bb = *BI;
        BBListReplace(bb->Preds, splitBB, newBB);
    }
    newBB->Succs.splice(newBB->Succs.end(), splitBB->Succs);
    newBB->Preds.push_back(splitBB);
    splitBB->Succs.clear();
    splitBB->Succs.push_back(newBB);
    G4_INST *gotoInst = getGotoInst(splitBB);
    if (gotoInst)
    {
        splitBB->pop_back();
        newBB->push_back(gotoInst);
    }

    // insert it into BBs
    BI = findBB(BBs, splitBB);
    ++BI;
    BBs->insert(BI, newBB);
    G4_BB *phySucc = splitBB->getPhysicalSucc();
    splitBB->setPhysicalSucc(newBB);
    newBB->setPhysicalPred(splitBB);
    newBB->setPhysicalSucc(phySucc);
    if (phySucc)
    {
        phySucc->setPhysicalPred(newBB);
    }

    ANodeBB *ndbb = new ANodeBB(newBB);
    ndbb->anodeId = getNextANodeId();
    setANodeBB(ndbb, newBB);

    ANode *splitNode = getANodeBB(splitBB);
    ANode *tmp = splitNode->parent;
    while (tmp)
    {
        if (tmp->getEndBB() == splitBB)
        {
            tmp->setEndBB(newBB);
        }
        tmp = tmp->parent;
    }

    adjustBBId(newBB);

    return ndbb;
}

// Start from bb (inclusive), search backward to find the first original BB.
G4_BB *CFGStructurizer::findInsertAfterBB(G4_BB *bb)
{
    G4_BB *insertAfter = bb;
    while (insertAfter && insertAfter->getId() >= numOfBBs)
    {
        insertAfter = insertAfter->getPhysicalPred();
    }
    return insertAfter;
}

// Re-connect every predecessors of "from" to its new successor "to". Update
// "to"'s preds and its new predecessors' succs. Note that "from"'s preds are
// left unchanged.
void CFGStructurizer::reConnectAllPreds(ANode *from, ANode *to)
{
    for (ANList::iterator I = from->preds.begin(), E = from->preds.end();
        I != E; ++I)
    {
        ANode *pred = *I;
        ANListReplace(pred->succs, from, to);
        to->preds.push_back(pred);
    }
}

// Re-connect every successors of "from" to its new predecesor "to". Update
// "to"'s succs and its new successors' preds. Note that "from"'s succs are
// left unchanged.
void CFGStructurizer::reConnectAllSuccs(ANode *from, ANode *to)
{
    for (ANList::iterator I = from->succs.begin(), E = from->succs.end();
        I != E; ++I)
    {
        ANode *succ = *I;
        ANListReplace(succ->preds, from, to);
        to->succs.push_back(succ);
    }
}

// This function is to merge ANStack nodes into the new node during HG
// construction.
void CFGStructurizer::ANStack_reconstruct(ANodeHG *& node, ANodeBB *ndbb)
{
    ANodeHG *newNode = getEnclosingANodeHG(ndbb);
    if (newNode->ANStackIx == ANStack.size() - 1)
    {
        // No need to do ANStack reconstruction
        return;
    }

    // Merge all nodes in ANStack[newNode->ANStackIx+1 : ANStack.size() - 1] into newNode
    G4_BB *exit = newNode->getExitBB();
    G4_BB *end = newNode->getEndBB();

    // Merging from ANStackIx to the top of ANStack is needed to make
    // sure the order of children is correct.
    for (uint32_t i = newNode->ANStackIx + 1, size = ANStack.size(); i < size; ++i)
    {
        ANodeHG *nd = (ANodeHG *)ANStack[i];
        getNewRange(end, exit, nd->getEndBB(), nd->getExitBB());
        for (ANList::iterator I = nd->children.begin(), E = nd->children.end();
            I != E; ++I)
        {
            ANode *tmp = *I;
            tmp->parent = newNode;
            newNode->children.push_back(tmp);
        }
    }
    newNode->isLoopCandidate = false;
    newNode->setExitBB(exit);
    newNode->setEndBB(end);
    uint32_t n = (uint32_t) ANStack.size() - newNode->ANStackIx - 1;
    if (n > 0)
    {
        for (uint32_t i = 0; i < n; ++i)
        {
            ANode *nd = ANStack.back();

            // Make sure to delete from newNode' children
            ANListErase(newNode->children, nd);

            ANStack.pop_back();
            delete nd;
        }
    }

    node = newNode;
    return;
}

// For case like:
//   do: ...
//       if (p) {
//         break;
//       else
//         ...
//       endif
//       .....
//   while
//
//   if-else-endif does not form a hammock because of break (it has two exits,
//   one is the target of break, the other is the BB right after endif). we
//   will need to make break as "no-op" instruction and then re-run
//   constructPST() on the body of the while loop.
//
//  For now, implement a poor-man's break construction. More general code
//  will come after.
//
void CFGStructurizer::reConstructDoWhileforBreak(ANodeHG *whileNode)
{
    G4_BB *exitbb = whileNode->getExitBB();
    ANList::iterator IB = whileNode->children.begin();
    ANList::iterator IE = whileNode->children.end();
    ANList::iterator II = IB;
    BB_LIST activeJoinBBs;
    while (II != IE)
    {
        ANode *nd = *II;
        while (!activeJoinBBs.empty())
        {
            G4_BB *bb = activeJoinBBs.front();
            if (isBefore(nd->getBeginBB(), bb))
            {
                break;
            }
            activeJoinBBs.pop_front();
        }

        G4_BB *ebb = nd->getExitBB();
        if (ebb != exitbb)
        {
            if (ebb)
            {
                bb_oinsert(activeJoinBBs, ebb);
            }
            ++II;
            continue;
        }

        if (nd == whileNode->children.back())
        {
            ++II;
            continue;
        }

        whileNode->setHasBreak(true);
        if (nd->getType() != AN_BB)
        {
            // don't handle it.
            whileNode->setAllowSCF(false);
            break;
        }

        ANodeBB *ndbb = (ANodeBB*)nd;
        if (II == IB || activeJoinBBs.empty())
        {
            // Break isn't inside any possible gotos (conservative)
            ndbb->setHasBreak(true);
            ++II;
            continue;
        }

        if (ndbb->preds.size() != 1)
        {
            whileNode->setAllowSCF(false);
            break;
        }

        ANode *exitNode = ndbb->succs.back();
        if (whileNode->isMember(exitNode))
        {
            exitNode = ndbb->succs.front();
        }
        ANode *pred = ndbb->preds.back();
        ANList::iterator IPred = II;
        --IPred;
        ANode *phyPred = *IPred;
        ANList::iterator IPredPred = IPred;
        ANode *phyPredPred = nullptr;
        if (IPredPred != IB)
        {
            --IPredPred;
            phyPredPred = *IPredPred;
        }

        ANList::iterator ISucc = II;
        ++ISucc;
        ANode *phySucc = (ISucc != IE) ? *ISucc : nullptr;
        ANode *predTarget = ANListGetOther(pred->succs, ndbb);

        // Handle two cases:
        //  Case 1 (if-then-endif with break)
        //    BB0:
        //       [p] goto BB2
        //    BB1:
        //       break;
        //    BB2:
        //
        // Case 2: (if-then-else-endif with break)
        //    BB0:
        //       [p] goto BB2
        //    BB1:
        //       goto BB3;
        //    BB2:
        //       break;
        //    BB3:
        //

        // case 1
        if (phyPred == pred && pred->getType() == AN_BB &&
            predTarget && predTarget == phySucc)
        {
            bool calNewIB = (IPred == IB);

            ANodeHG *newIf = new ANodeHG(AN_IF_THEN_ENDIF);
            newIf->anodeId = getNextANodeId();

            newIf->parent = whileNode;
            whileNode->children.erase(IPred);
            whileNode->children.erase(II);
            whileNode->children.insert(ISucc, newIf);
            reConnectAllPreds(pred, newIf);
            newIf->succs.push_back(predTarget);
            newIf->succs.push_back(exitNode);
            ANListReplace(exitNode->preds, ndbb, newIf);
            ANListReplace(predTarget->preds, pred, newIf);

            newIf->children.push_back(pred);
            newIf->children.push_back(ndbb);
            newIf->setBeginBB(pred->getBeginBB());
            newIf->setEndBB(ndbb->getEndBB());
            newIf->setExitBB(predTarget->getBeginBB());

            ndbb->setHasBreak(true);
            newIf->setHasBreak(true);
            ndbb->setAllowSCF(true);
            newIf->setAllowSCF(true);

            if (calNewIB)
            {
                IB = whileNode->children.begin();
            }
            II = ISucc;
        }
        // Case 2
        else if (predTarget && phyPredPred && phyPredPred == pred &&
            phyPred == predTarget && pred->getType() == AN_BB &&
            phyPred->succs.size() == 1 && phyPred->succs.front() == phySucc)
        {
            bool calNewIB = (IPredPred == IB);

            ANodeHG *newIf = new ANodeHG(AN_IF_THEN_ELSE_ENDIF);
            newIf->anodeId = getNextANodeId();

            newIf->parent = whileNode;
            whileNode->children.erase(IPredPred);
            whileNode->children.erase(IPred);
            whileNode->children.erase(II);
            whileNode->children.insert(ISucc, newIf);
            reConnectAllPreds(pred, newIf);
            newIf->succs.push_back(phySucc);
            newIf->succs.push_back(exitNode);
            ANListReplace(exitNode->preds, ndbb, newIf);
            ANListReplace(predTarget->preds, pred, newIf);

            newIf->children.push_back(pred);
            newIf->children.push_back(phyPred);
            newIf->children.push_back(ndbb);
            newIf->setBeginBB(pred->getBeginBB());
            newIf->setEndBB(ndbb->getEndBB());
            newIf->setExitBB(phySucc->getBeginBB());

            ndbb->setHasBreak(true);
            newIf->setHasBreak(true);
            ndbb->setAllowSCF(true);
            newIf->setAllowSCF(true);

            if (calNewIB)
            {
                IB = whileNode->children.begin();
            }
            II = ISucc;
        }
        else
        {
            whileNode->setAllowSCF(false);
            break;
        }
    }

    if (!whileNode->getAllowSCF())
    {
        // If not allowed, propagate this attribute to all
        // its children with break inside.
        ANList::iterator II = IB;
        for (II = IB; II != IE; ++II)
        {
            ANode *nd = *II;
            if (nd->getHasBreak())
            {
                nd->setAllowSCF(false);
            }
        }
    }
}

//
// PST : program structure tree.
//         ANode -- PST's node. It's an abstract class.
//           ANodeBB : ANode -- one for each BB (leaf node)
//           ANodeHG : ANode -- Hammock graph (group of ANodeBB)
//                              (non-leaf node)
//   ANodeBB is for BB, while ANodeHG is a Hammock graph that is
//   composed of seveal BBs (mostly more than one, it could be one).
//   The Hammock graph defined in this implementation is the minimum
//   hammock graph for any goto BB (BB that has goto inst). Nesting
//   relation is represented with ANode's parent field and ANodeHG's
//   children (ANodeBB cannot have children, it is a leaf node). Given
//   two hammock graphs, they either have no common ANodes or one is
//   completely enclosed within the other. The Whole program is a Hammock,
//   and this node is the PST's root node.
//
//   In the implementation, we don't really create ANode for the PST's
//   root. Instead, we construct only hammock children of the Root,
//   denoted by topANodes. This topANodes are refered to as top-level
//   nodes. (Note that for any BB that is b/w any goto BB and its target
//   BB in the program order, an ANode is created for it; otherwise, it
//   has no ANode.)
//
//   Note that the children of each ANodeHG is the ordered list of ANode
//   (in BB order).
//
// ACFG : Abstract Control Flow Graph of ANode (ACFG):
//   This is a CFG of ANode, used in the process of constructing PST.
//   It can be viewed as the top-level PST on-the-fly (ignoring any nodes
//   that are children of the top level nodes). By "on-the-fly", it means
//   that the ACFG is constantly changing. Once a group of nodes in ACFG
//   is formed into a single ANodeHG, this group is replaced by this new
//   single ANodeHG, and the group of nodes is no longer in the ACFG. Such
//   node condensing will make finding interesting program structures (if,
//   while, etc.) easier. The process continues until all BBs are processed.
//   All the nodes in the final ACFG is the actually top-level nodes of the PST.
//   (In another word, ACFG will always refect the control flow among the
//    outmost parent nodes of all ANodeBBs.)
//
//   ACFG's control-flow edges : represented by ANode's preds & succs.
//
//   Note that ACFG edges within any ANodeHG via preds & succs should be still
//   valid, but outgoing/incoming edges are no longer valid. To get a right
//   ANode target, using BB's preds/succs as they are always up to date.
//
// input
//    [IB, IE) : denotes BB from IB to IE, containing IB, but not IE.
// Results
//    topANodes
//
void CFGStructurizer::constructPST(BB_LIST_ITER IB, BB_LIST_ITER IE)
{
    // This algorithm assumes that IE is loop-invariant. For the code
    // that might insert/delete BB, pay attention to this rule!

    // ANStack is used to keep all pending ANodes. It also reflects
    // possible nesting relation (ANStack[i] could be the parent of
    // ANStack[i+1]).
    ANodeHG *node = nullptr;
    BB_LIST_ITER II = IB;
    while (II != IE)
    {
        G4_BB *bb = *II;
        ANodeBB *ndbb = getANodeBB(bb);

        // Do the following cases in order. (Note that bb should be part of
        // the current pending ANode (top of ANStack), although it could
        // be the beginning node of the new inner ANode.)
        //
        // 1: If bb is the target of some gotos, that is, the end of
        //    some CGs, merge the current bb into ANStack nodes of
        //    which those CGs belong to (as they should be in the same
        //    hammock).
        //    Also, any backward goto's begin and end BB should be in
        //    the same hammock. Once merging is done (if any), go on to
        //    the next step in the loop.
        //
        // 2: If bb is the begin of CGs, need to create new HG for each
        //    cg that is associated with a backward goto, a forward
        //    conditional goto, and the root (first) forward unconditional
        //    goto.  No new HG is created for a non-root unconditional
        //    goto (see details in code). This is where an inner HG is formed.)
        //
        // 3: Add bb into this node as it is part of node. If bb can
        //    be merged with its pred, merge it. The new node's type
        //    must be type AN_SEQUENCE.
        //
        // 4: Check if bb is the end of the node.  If it is, finalize the
        //    node. If it is not, go on to process the next BB in the next
        //    iteration (This implies that if the next BB should be part
        //    of the current pending HG in ANStack).
        //

        // case 1.
        if (node)
        {
            ANStack_reconstruct(node, ndbb);
        }

        // case 2
        CGList cgs;
        if (getCGBegin(bb, cgs))
        {
            // If bb is the begin of more than one ANode on ANStack, ndbb
            // should be added only into innermost ANode. Keep the
            // following in mind:
            //   1. (a) bb's last inst is a goto.
            //          ndbb should be added to the current node (ANStack's
            //          top) if it exists and the goto is a unconditional goto;
            //          otherwise, a new hammock node is created and ndbb
            //          is added into this new hammock node (thus for both
            //          conditional goto and a top node).
            //      (b) bb's last inst is not a goto
            //          add ndbb in the current node.
            //   2. If there are several backward gotos with bb as the begin
            //      BB, ndbb is added only into the ANode corresponding to the
            //      innermost backward goto. (All backward gotos should have their
            //      new ANodes created.)
            ControlGraph *cg;

            // For backward cg, we will only create ANodeHG if the backward cg
            // is a loop candidate. If it is not candidate, extend the current
            // node with this cg.
            for (uint32_t i = (uint32_t) cgs.size() - 1; i > 0; --i)
            {
                // This must be non-innermost backward goto, don't add ndbb to
                // the new node, ie. passing nullptr(2nd arg) to ANodeHG's ctor.
                cg = cgs.back();
                MUST_BE_TRUE(cg->isBackward,
                    "Additional CG must be for backward gotos");
                if (!cg->isLoopCandidate)
                {
                    assert(node && "Node should not be empty here!");
                    extendNode(node, cg);
                }
                else
                {
                    ANodeHG *parent = node;
                    node = new ANodeHG(cg, nullptr);
                    node->anodeId = getNextANodeId();
                    node->parent = parent;
                    node->isLoopCandidate = true;
                    if (parent)
                    {
                        parent->children.push_back(node);
                    }
                    ANStack_push(node);
                }
                cgs.pop_back();
                delete cg;
            }

            // First one in cgs, it is either an innermost backward goto or a forward goto
            cg = cgs.back();
            cgs.pop_back();
            if (cg->isBackward || cg->gotoInst->getPredicate() != nullptr || node == nullptr)
            {
                if (cg->isBackward && !cg->isLoopCandidate)
                {
                    assert(node && "Node should not be empty at this point!");
                    extendNode(node, cg);
                }
                else
                {
                    // New HG node created and pushed on ANStack
                    ANodeHG *parent = node;
                    node = new ANodeHG(cg, ndbb);
                    node->anodeId = getNextANodeId();
                    node->parent = parent;
                    if (cg->isBackward)
                    {
                        node->isLoopCandidate = true;
                    }
                    if (parent)
                    {
                        parent->children.push_back(node);
                    }
                    ANStack_push(node);
                }
            }
            else
            {
                // For an unconditional goto, just add it into the current ANodeHG.
                G4_BB *exit = node->getExitBB();
                G4_BB *end = node->getEndBB();
                getNewRange(end, exit, ndbb->getEndBB(), ndbb->getExitBB());
                node->setExitBB(exit);
                node->setEndBB(end);
            }
            delete cg;
        }
        else if (!node)
        {
            // Not a goto BB, skip.
            ++II;
            continue;
        }

        // case 3
        // Add this new ANodeBB into node and also check if it can be
        // merged with the previous one to form a new SEQUENCE hammock.
        if (ndbb->parent != node)
        {
            node->children.push_back(ndbb);
            ndbb->parent = node;
        }
        (void)mergeWithPred(ndbb);

        // Step 4
        if (isBefore(bb, node->getEndBB()))
        {
            ++II;
            continue;
        }

        // At this point, bb == node->getEndBB().
        // Found a HG if
        //    1. bb is a unconditional backward goto, or
        //    2. bb's fall-thru is the same as node's exitBB
        G4_INST *gotoInst = getGotoInst(bb);
        bool uncondBackwardGoto =
            gotoInst && bb->Succs.size() == 1 && gotoInst->asCFInst()->isBackward();
        if (!(uncondBackwardGoto ||
              ndbb->succs.size() == 0 || node->getExitBB() == nullptr ||
              ndbb->succs.front()->getBeginBB() == node->getExitBB()))
        {
            G4_BB *next = bb->getPhysicalSucc();
            if (next)
            {
                node->setEndBB(next);
            }
            ++II;
            continue;
        }

        // At this point, node is a MHG.

        // The top of ANStack is an MHG, poping and condensing it into a new
        // ANodeHG. After condensing, it might expose merging opportunity,
        // if so, merge the newly condensed node with its predecessor. Once
        // merging and/or condensing is performed, check if the new top of
        // ANStack is an MHG, if so, doing the above again. Continue this
        // until the top of ANStack is not a HG.
        //
        // At this stage, if a MHG's exit isn't its physical next,  add a
        // landingBB as its next physical BB.  Note that the landingBB is
        // the new exit BB, which is not part of this MHG. For now, we will
        // only generate a single BB at most after each original BB. In
        // another word, if more than one MHG share the same exit,  it will
        // share this same landingBB (creating more than one BB could make
        // jmpi/goto inefficient - two goto/jmpi instead of one.
        // (May need a cfg simplification after structurizer.)
        //
        bool isTopHG = true;

        // Keep track of the outmost SCF so that we can add a landingBB
        // only for the oustmost SCF if needed.
        ANodeHG *outmostSCF = nullptr;
        ANodeHG *lastHG = nullptr;

        G4_BB *exitBB = node->getExitBB();
        while (isTopHG)
        {
            // a Hammock has been detected. Set HG to the right type.
            finalizeHG(node);

            // record the outmost structured CF MHG
            if (node->isSCF())
            {
                outmostSCF = node;
            }

            // check if it can be merged with the previous ANode. Note that
            // we only need to check its immediate predecessor !
            ANode *nd = mergeWithPred(node);
            lastHG = (ANodeHG*)nd;

            isTopHG = false;
            ANStack_pop();
            if (!ANStack.empty())
            {
                node = (ANodeHG *)ANStack.back();
                G4_BB *exit = node->getExitBB();
                G4_BB *end = node->getEndBB();
                getNewRange(end, exit, nd->getEndBB(), nd->getExitBB());
                node->setEndBB(end);
                node->setExitBB(exit);

                if (isBefore(bb, end) || exit != exitBB)
                {
                    // No more HG that ends with this exitBB
                    continue;
                }
                isTopHG = true;
            }
        }  // while (istopHG)

        if (outmostSCF)
        {
            // Add a landingBB and adjust control flows (both CFG and ACFG).
            // The new BB will be processed later (so no PST updating is
            // needed here).
#if 0
            // TODO: for performance, we should only add landingBB for outmostSCF,
            // however, doing so would have to special-handling the landingBB as
            // it should merge with outmostSCF, which is not in ACFG anymore (not
            // a top node anymore). So far, the this function is able to handle
            // node in ACFG only.
            if (!outmostSCF->isExitPhySucc())
            {
                (void)addLandingBB(outmostSCF, II, true);
            }
#else
            if (!lastHG->isExitPhySucc() && lastHG->getExitBB())
            {
                (void)addLandingBB(lastHG, II, true);
            }
#endif
         }

        if (ANStack.empty() && node)
        {
            // This is a top ANode, add it.
            topANodes.push_back(node);
            node = nullptr;
        }
        ++II;
    }  // while (!ANStack.empty())

    MUST_BE_TRUE(ANStack.empty(), "ANodeHG stack is incorrectly formed");
#ifdef _DEBUG
#if  0
        dumpcfg();
        dumpacfg();
#endif
#endif
    return;
}

// return true if bb0 is before bb1 in the BB list.
bool CFGStructurizer::isBefore(G4_BB *bb0, G4_BB *bb1)
{
    MUST_BE_TRUE(bb0 && bb1, "BB ptrs should not be null");
    if (bb0 == bb1)
    {
        return false;
    }

    // common case first
    uint32_t id0 = bb0->getId();
    uint32_t id1 = bb1->getId();
    MUST_BE_TRUE(id0 != id1, "Two different BBs must have different Ids");

    if (id0 < numOfBBs && id1 < numOfBBs)
    {
        return id0 < id1;
    }

    uint32_t insertAtId0 = id0 >= numOfBBs ? getInsertAfterBB(bb0)->getId() : id0;
    uint32_t insertAtId1 = id1 >= numOfBBs ? getInsertAfterBB(bb1)->getId() : id1;
    if (insertAtId0 != insertAtId1)
    {
        return insertAtId0 < insertAtId1;
    }

    // Now, the case for a BB and its new BB or two new BBs with the same
    // insertion position.
    //
    // Since we make sure the larger the id, the later the position during
    // inserting new BBs. We will simply check their ids
    return id0 < id1;
}


// Insert newNode into parent and insertion place is after node
// if isAfter is true, or before node if isAfter is false.
void CFGStructurizer::PSTAddANode(
    ANodeHG *parent, ANode *node, ANode *newNode, bool isAfter)
{
    newNode->parent = parent;
    if (parent)
    {
        ANList::iterator I1 = findANode(parent->children, node);
        MUST_BE_TRUE(I1 != parent->children.end(),
            "Child node should be in parent's children list");
        if (isAfter)
        {
            ++I1;
        }
        parent->children.insert(I1, newNode);
    }
}

// adjustBBId will make sure that all the new BB's with the same insertion
// position should have ids in the increasing order.
//
// Before calling this function, newBB has to be inserted into
// the BBs and its physicalSucc/Pred has been set up correctly.
void CFGStructurizer::adjustBBId(G4_BB *newbb)
{
    // newBB's id is the largest at this moment. Make sure it is in order
    uint32_t id = newbb->getId();
    G4_BB *pred = newbb;
    G4_BB *next = newbb->getPhysicalSucc();
    while (next && next->getId() >= numOfBBs)
    {
        pred->setId(next->getId());
        pred = next;
        next = next->getPhysicalSucc();
    }
    pred->setId(id);
}

G4_BB *CFGStructurizer::createBBWithLabel()
{
    G4_BB *newBB = CFG->createNewBB();

    // Create a label for the new BB
    std::string str("label_cf_");
    str += std::to_string(newBB->getId());
    G4_Label* lab = CFG->builder->createLabel(str, LABEL_BLOCK);
    G4_INST *labelInst = CFG->builder->createLabelInst(lab, false);
    newBB->push_front(labelInst);
    return newBB;
}

// Insert an inst at the begin after a label instruction.
void CFGStructurizer::insertAtBegin(G4_BB* aBB, G4_INST *anInst)
{
    INST_LIST_ITER I = aBB->begin();
    MUST_BE_TRUE((*I)->opcode() == G4_label,
                 "The 1st inst of BB must be a label inst");
    ++I;
    aBB->insertBefore(I, anInst);
}

void CFGStructurizer::setJoinJIP(G4_BB* joinBB, G4_BB* jipBB)
{
    G4_INST *firstInst = joinBB->getFirstInst();
    MUST_BE_TRUE(firstInst, "Missing a join inst in join BB!");
    if (firstInst &&
        (firstInst->opcode() == G4_join || firstInst->opcode() == G4_endif))
    {
        G4_Label *jipLabel = jipBB ? jipBB->getLabel() : nullptr;
        firstInst->asCFInst()->setJip(jipLabel);
        return;
    }
    MUST_BE_TRUE(false, "Missing Join/endif instruction in join BB!");
}

void CFGStructurizer::convertChildren(ANodeHG *nodehg, G4_BB *nextJoinBB)
{
    // JIP setting of Join instruction:
    //   activeJoinBBs : a list of join BBs in the increasing order of BB layout,
    //                   which are after the BB being processed currently.
    //
    //   The join BB is the BB with join instruction as its first instruction.
    //   If the current BB is the first join BB (if exists) in acticveJoinBBs,
    //   its JIP will be set to the 2nd join in activeJoinBBs. Once its JIP is
    //   set, this BB is removed from activeJoinBBs.
    //
    //   activeJoinBBs keeps a list of join BBs whose jips needs to be set.
    //   It works like this: for each child ANode, if its exit BB is a join BB,
    //   add it into activeJoinBBs (we check if a BB is a join BB by checking
    //   if the BB has join instruction in it). As conversion is done bottom-up,
    //   all children must have been converted before their parents. This means
    //   that a join should be inserted in a child's exit BB if that child does
    //   need join. But that join's JIP is not known until the next join is found.
    //   Adding unknown-JIP join into activeJoinBBs so that it will get set when
    //   next next join is reached.
    //
    BB_LIST activeJoinBBs;
    if (nextJoinBB)
    {
        // The incoming joinBB is a special one, it could
        // be a BB with its else/endif/while/join.
        activeJoinBBs.push_back(nextJoinBB);
    }

    // If nodehg is DO_WHILE, its end should have been processed in convertDoWhile()
    // before invoking this function, hence, skip the end BB.
    ANList::iterator II = nodehg->children.begin();
    ANList::iterator IE = nodehg->children.end();
    if (nodehg->getKind() == ANKIND_SCF && nodehg->getType() == AN_DO_WHILE)
    {
        if (nodehg->children.size() > 1)
        {
            --IE;
            ANode *loopWhileNode = *IE;
            loopWhileNode->setVisited(true);
        }
        else
        {
            II = IE;
        }
    }

    for (; II != IE; ++II)
    {
        ANode *nd = *II;
        G4_BB *begin = nd->getBeginBB();

        G4_BB *nextjoin = nullptr;
        while (!activeJoinBBs.empty())
        {
            nextjoin = activeJoinBBs.front();
            if (begin == nextjoin || isBefore(nextjoin, begin))
            {
                // nextjoin has been reached. Set its JIP.
                activeJoinBBs.pop_front();
                G4_BB *tmp =
                    !activeJoinBBs.empty() ? activeJoinBBs.front() : nullptr;
                setJoinJIP(nextjoin, tmp);
            }
            else
            {
                break;
            }
        }

        // For loops, need to handle the backward goto and may insert join
        for (BB_LIST_ITER I1 = begin->Preds.begin(), E1 = begin->Preds.end();
            I1 != E1; ++I1)
        {
            // Don't process loopWhileNode if it is as it has been processed
            G4_BB *bb = *I1;
            G4_INST *gotoInst = getGotoInst(bb);
            ANodeBB *ndbb = getANodeBB(bb);
            ANode *nd1 = ndbb->getChildANode(nodehg);
            if (nd1 && nd != nd1 && gotoInst && !nd1->isVisited() &&
                gotoInst->asCFInst()->isBackward() &&
                bb->Succs.back() == begin)
            {
                convertPST(nd1, nullptr);
                nd1->setVisited(true);
                G4_BB *loopJoinBB = bb->getPhysicalSucc();
                if (loopJoinBB && isJoinBB(loopJoinBB))
                {
                    // Add loop's join bb into activeJoinBBs
                    if (findBB(&activeJoinBBs, loopJoinBB) == activeJoinBBs.end())
                    {
                        bb_oinsert(activeJoinBBs, loopJoinBB);
                    }
                }
            }
        }

        // Skip backward goto ANode, as it has been handled already
        if (nd->isVisited())
        {
            continue;
        }

        nextjoin = activeJoinBBs.empty() ? nullptr : activeJoinBBs.front();

        (void)convertPST(nd, nextjoin);
        nd->setVisited(true);

        G4_BB *joinbb = nd->getExitBB();
        // Special case for ANodeBB of a break.
        //   As its exit BB is still the original target (loop exit), there is
        //   no join instruction needed at the target as it is implicit loop
        //   join place. Here, just set joinbb to nullptr.
        //   (need to refactor the code as SCF (except SEQUENCE) should not
        //    have a join in its exit.)
        if (nd->getType() == AN_BB && nd->getKind() == ANKIND_SCF && nd->getHasBreak())
        {
            joinbb = nullptr;
        }

#if 0
        if (nd->getType() == AN_BB && begin->size() > 0)
        {
            G4_INST *gotoInst = begin->back();
            MUST_BE_TRUE(false, "Backward goto should have ben processed");
            if (gotoInst->opcode() == G4_goto && gotoInst->isBackward())
            {
                //
                MUST_BE_TRUE(false, "Backward goto should have ben processed");
                joinbb = begin->Succs.front();
            }
        }
#endif
        if (joinbb && isJoinBB(joinbb))
        {
            // Add exit into activeJoinBBs
            if (findBB(&activeJoinBBs, joinbb) == activeJoinBBs.end())
            {
                bb_oinsert(activeJoinBBs, joinbb);
            }
        }
    }

    uint32_t sz = (uint32_t)activeJoinBBs.size();
#if 0
    // temporarily turn off assert as it is too conservative and miss
    // some cases, thus genrate false assertion!

    // activeJoinBBs should have the following at this moment:
    //    1. nextJoinBB
    //    2. the nodehg's exit(s).
    //       nodehg is a hammock, so it should have a single exit. However,
    //       for something like "if (c) break endif", and nodehg is somehow
    //       decided to be non-SCF (ie converted to goto), nodehg will have
    //       two exits.
    //  [todo: have a simple/better way to handle this]
    //  Note that the outer node of PST may recalculate JIP.
    uint32_t NEntries = nextJoinBB ? 2 : 1;
    if (nodehg->getHasBreak() && !nodehg->getAllowSCF())
    {
        ++NEntries;
    }
    assert(sz <= NEntries && "Something is wrong!");
#endif
    if (sz > 0)
    {
        // lastJoin will never be nullptr.
        G4_BB *lastJoin = activeJoinBBs.front();
        activeJoinBBs.pop_front();
        for (--sz; sz > 0; --sz)
        {
            if (lastJoin == nextJoinBB)
            {
                // don't need to process it further as the outer
                // ANode will handle nextJoinBB and nodes after it.
                break;
            }
            G4_BB *JipBB = activeJoinBBs.front();
            activeJoinBBs.pop_front();
            setJoinJIP(lastJoin, JipBB);
            lastJoin = JipBB;
        }
        // Handle the last one.
        if (lastJoin != nextJoinBB)
        {
            // set the last to nullptr
            setJoinJIP(lastJoin, nullptr);
        }
    }
    return;
}

// TODO: more comments
// If then block and else block may have gotos, we will insert join at the end of
// then or else blocks as the new join exit.
void CFGStructurizer::convertIf(ANodeHG *node, G4_BB *nextJoinBB)
{

//#if defined(_DEBUG)
//    useDebugEnv();
//#endif

    // nextJoinLabel is used to set JIP for endif instruction
    G4_Label *nextJoinLabel = nextJoinBB ? nextJoinBB->getLabel() : nullptr;
    G4_BB *begin = node->getBeginBB();
    G4_INST *gotoInst = begin->back();
    G4_ExecSize execSize = gotoInst->getExecSize();
    execSize = execSize > 1 ? execSize : kernelExecSize;
    ANList::iterator II = node->children.begin();
    ANode *thenNode = *(++II);  // children[1]
    G4_BB *end = node->getEndBB();
    G4_BB *exit = node->getExitBB();

    MUST_BE_TRUE(end->getPhysicalSucc() == exit,
        "Landing BB should have been inserted during construction of hammock graph");

    bool isUniform = isGotoScalarJmp(gotoInst);
    ANodeKind kind = ANKIND_GOTOJOIN;
    if (doScalarJmp && isUniform)
    {
        kind = ANKIND_JMPI;
    }
    else if (doStructCF && node->getAllowSCF())
    {
        if (node->getHasBreak())
        {
            ANodeHG *innermostWhile = getInnerMostWhile(node);
            MUST_BE_TRUE(innermostWhile, "if-endif with break isn't inside a while");
            if (innermostWhile->getKind() == ANKIND_SCF)
            {
                kind = ANKIND_SCF;
            }
        }
        else
        {
            kind = ANKIND_SCF;
        }
    }
    node->setKind(kind);


    // If this IF-ENDIF does not have break, there must be at least
    // one active channel enterning IF-ENDIF and at least one active
    // channel at the end of "IF-ENDIF". Thus endif's JIP will never
    // be used.
    if (!node->getHasBreak())
    {
        nextJoinLabel = nullptr;
    }

    if (node->getHasBreak() && kind == ANKIND_GOTOJOIN)
    {
        // if break isn't honored, if-endif should not be
        // honored and should be treated as non if/endif node.
        convertChildren(node, nextJoinBB);
        return;
    }

    ANodeBB *exitndbb = getANodeBB(exit);
    if (kind != ANKIND_JMPI && !isANodeOnlyPred(exit, node) &&
        (exitndbb->isJmpiTarget ||
         (kind == ANKIND_GOTOJOIN && !isJoinBB(exit)) ||
         (kind == ANKIND_SCF && !isBBLabelAvailable(exit))))
    {
        // To have a new BB for either endif or join
        exitndbb = addLandingBB(node, BBs->end(), false);

        // Add it into PST. Will need {[SEQUENCE] Node, exitndbb} as
        // a new ANode to replace node.
        PSTAddANode((ANodeHG*)node->parent, node, exitndbb, true);

        exit = exitndbb->getBeginBB();
    }
    G4_Label *endifLabel = exit->getLabel();

    switch (node->type)
    {
    case AN_IF_THEN_ENDIF:
    {
        if (kind == ANKIND_JMPI)
        {
            CFG->convertGotoToJmpi(gotoInst);
            exitndbb->isJmpiTarget = true;
            (void)convertPST(thenNode, nextJoinBB);
            return;
        }
        else if (kind == ANKIND_GOTOJOIN)
        {
            // Generate goto/join
            generateGotoJoin(begin, exit, exit);
            setJoinJIP(exit, nextJoinBB);
            (void)convertPST(thenNode, node->getHasBreak() ? exit : nullptr);
            return;
        }

        G4_INST* endifInst = CFG->builder->createInternalCFInst(
            NULL, G4_endif, execSize, nextJoinLabel, NULL, InstOpt_NoOpt);

        insertAtBegin(exit, endifInst);

        // jip = uip = endif
        G4_Predicate* pred = gotoInst->getPredicate();
        MUST_BE_TRUE(pred != NULL, "if must have non-null predicate");
        pred->setState(pred->getState() == PredState_Plus ? PredState_Minus : PredState_Plus);
        G4_INST* ifInst = CFG->builder->createInternalCFInst(
            pred, G4_if, execSize, endifLabel, endifLabel, InstOpt_NoOpt);
        begin->pop_back();
        begin->push_back(ifInst);
        if (isUniform)
        {
            ifInst->asCFInst()->setUniform(true);
        }

        if (thenNode->isHammock())
        {
            //  if:
            //       goto endif
            //       ...
            //       goto endif
            //  else
            //       ...
            //  endif:
            //
            //  changed to:
            //  if:
            //      goto joinbb
            //      ...
            //      goto joinbb
            // joinbb:   <--- insert join instruction here
            // else
            //      ...
            // endif:
            //
            ANodeBB *ndbb = addLandingBB((ANodeHG*)thenNode, BBs->end(), false);

            // For now, just insert it into node (should create SEQ)
            PSTAddANode(node, thenNode, ndbb, true);
        }
        (void)convertPST(thenNode, node->getHasBreak() ? exit  : nullptr);

        break;
    }

    case AN_IF_THEN_ELSE_ENDIF:
    {
        ANode *elseNode = *(++II); // children[2];
        G4_BB *thenLastBB = thenNode->getEndBB();
        G4_INST *thenLastInst = thenLastBB->back();

        // May have the case in which thenLastInst isn't goto
        //  if (uniformCond)
        //     return;
        //  else
        //     ...
        //  endif
        MUST_BE_TRUE(
            (thenLastInst->opcode() != G4_goto &&
             thenLastBB->Succs.size() == 0) ||
            (thenLastInst->opcode() == G4_goto &&
             thenLastInst->getPredicate() == nullptr),
            "Goto in then block should be unconditional");

        if (kind == ANKIND_JMPI)
        {
            // goto's target is elseBB, and no need to create a new label.
            CFG->convertGotoToJmpi(gotoInst);
            {
                G4_BB *elsebeginbb = elseNode->getBeginBB();
                ANodeBB *elsendbb = getANodeBB(elsebeginbb);
                elsendbb->isJmpiTarget = true;
            }
            if (thenLastInst->opcode() == G4_goto)
            {
                CFG->convertGotoToJmpi(thenLastInst);
                exitndbb->isJmpiTarget = true;
            }

            (void)convertPST(thenNode, nextJoinBB);
            (void)convertPST(elseNode, nextJoinBB);
            break;
        }
        else if (kind == ANKIND_GOTOJOIN)
        {
            // Generate goto/join
            G4_BB *elseFirstBB = elseNode->getBeginBB();
            generateGotoJoin(begin, elseFirstBB, elseFirstBB);

            // convertPST(thenNode, elseFirstBB) will handle this
            //   generateGotoJoin(thenLastBB, elseFirstBB, exit);
            (void)convertPST(thenNode, elseFirstBB);
            (void)convertPST(elseNode, exit);
            setJoinJIP(elseFirstBB, exit);
            setJoinJIP(exit, nextJoinBB);

            return;
        }

        // Add endif instruction into exit BB, else into the beginning
        // BB of else part,  and replace goto in the begin BB with if
        // instruction.
        auto endifInst = CFG->builder->createInternalCFInst(
            NULL, G4_endif, execSize, nextJoinLabel, NULL, InstOpt_NoOpt);

        insertAtBegin(exit, endifInst);

        G4_BB *elseFirstBB = elseNode->getBeginBB();
        G4_Label* elseLabel = elseFirstBB->getLabel();
        G4_Predicate* pred = gotoInst->getPredicate();
        MUST_BE_TRUE(pred != NULL, "if must have non-null predicate");
        pred->setState(pred->getState() == PredState_Plus ? PredState_Minus : PredState_Plus);

        // if instruction : jip = else_label, uip = endif
        G4_INST* ifInst = CFG->builder->createInternalCFInst(
            pred, G4_if, execSize, elseLabel, endifLabel, InstOpt_NoOpt);
        begin->pop_back();
        begin->push_back(ifInst);

        // else instruction: jip = uip = endif
        G4_BB *newThenLastBB = thenLastBB;
        if (thenNode->isHammock())
        {
            ANodeBB *ndbb = addLandingBB((ANodeHG*)thenNode, BBs->end(), false);

            // Correct way to update PST is as {[SEQ]thenNode, newThenLastBB}
            // and replace thenNode with this new SEQ node.
            // For now, it does not matter much, just use the following
            PSTAddANode(node, thenNode, ndbb, true);

            newThenLastBB = ndbb->getBeginBB();
        }
        G4_INST *thenGoto = newThenLastBB->back();
        G4_INST* elseInst = CFG->builder->createInternalCFInst(
            NULL, G4_else, execSize, endifLabel, endifLabel, InstOpt_NoOpt);
        if (thenGoto->opcode() == G4_goto)
        {
            newThenLastBB->pop_back();
        }
        newThenLastBB->push_back(elseInst);
        if (isUniform)
        {
            ifInst->asCFInst()->setUniform(true);
        }

        (void)convertPST(thenNode, node->getHasBreak() ? newThenLastBB : nullptr);

        if (elseNode->isHammock())
        {
            // Insert a join bb
            ANode *ndbb = addLandingBB((ANodeHG*)elseNode, BBs->end(), false);
            PSTAddANode(node, thenNode, ndbb, true);
        }
        // exit must be the physical next of this if-endif
        (void)convertPST(elseNode, node->getHasBreak() ? exit : nullptr);
        break;
    }
    default:
        MUST_BE_TRUE(false, "Unreachable, must be a wrong node type");
        break;
    }
    return;
}

void CFGStructurizer::convertDoWhile(ANodeHG *node, G4_BB *nextJoinBB)
{

//#if defined(_DEBUG)
//    useDebugEnv();
//#endif

    G4_BB *end = node->getEndBB();
    G4_INST *gotoInst = end->back();
    G4_ExecSize execSize = gotoInst->getExecSize();
    execSize = execSize > g4::SIMD1 ? execSize : kernelExecSize;
    G4_BB *head = node->getBeginBB();
    G4_BB *exit = node->getExitBB();

    // For break/continue(no continue for now), unfortunately hammock does
    // not work. Need to specialize it. A generic approach is to
    // change all break/continue into a single ANode with its targets ignored
    // and just use its physical next as its next. Then re-do hammock only for
    // loop's children (no while BB). If every single break/continue are
    // within the structured no-loop CF, then it can be break/continue.
    // Note that mix of goto-join with break/continue may be not working, so
    // need to check it out to avoid generating them.
    //

    bool isUniform = isGotoScalarJmp(gotoInst);
    ANodeKind ndkind = ANKIND_GOTOJOIN;
    if (doScalarJmp && isUniform &&
        (!doStructCF || !node->getHasBreak()))
    {
        // If loop has break, favor structured CF, not jmpi
        ndkind = ANKIND_JMPI;
    }
    else if (doStructCF && node->getAllowSCF())
    {
        ndkind = ANKIND_SCF;
    }
    node->setKind(ndkind);
    if (ndkind == ANKIND_JMPI)
    {
        // goto's target is head.
        CFG->convertGotoToJmpi(gotoInst);
        convertChildren(node, nextJoinBB);
    }
    else if (ndkind == ANKIND_SCF)
    {
        G4_Label* doLabel = head->getLabel();

        // Create a new BB to keep while. This new BB
        // would be in the same PST ANode as the original end.
        ANode *ndbb = addSplitBBAtEnd(end);
        node->children.push_back(ndbb);
        ndbb->parent = node;

        end = ndbb->getBeginBB();

        // jip = uip = do
        G4_INST *whileInst = CFG->builder->createInternalCFInst(
            gotoInst->getPredicate(), G4_while, execSize, doLabel, doLabel, InstOpt_NoOpt);
        end->pop_back();
        end->push_back(whileInst);
        if (isUniform)
        {
            whileInst->asCFInst()->setUniform(true);
        }

        convertChildren(node, node->getHasBreak() ? end : nullptr);
    }
    else
    {
        if (!isJoinBB(exit) && !canbeJoinBB(exit))
        {
            ANode *ndbb = addLandingBB(node, BBs->end(), false);

            // Make it simply by adding newexitbb into node->parent.
            // (May use {[SEQ] node, newexitbb} to replace.)
            PSTAddANode((ANodeHG*)node->parent, node, ndbb, true);

            exit = ndbb->getBeginBB();
        }
        generateGotoJoin(end, exit, exit);
        setJoinJIP(exit, nextJoinBB);
        getANodeBB(end)->setVisited(true);

        convertChildren(node, exit);
    }
}

void CFGStructurizer::generateGotoJoin(G4_BB *gotoBB, G4_BB *jibBB, G4_BB *joinBB)
{
    G4_INST *gotoInst = gotoBB->back();
    MUST_BE_TRUE(gotoInst && gotoInst->opcode() == G4_goto,
                 "gotoBB should have goto instruction");
    G4_Label *nextJoinLabel = jibBB ? jibBB->getLabel() : nullptr;
    G4_ExecSize execSize = gotoInst->getExecSize();
    execSize = execSize > g4::SIMD1 ? execSize : kernelExecSize;

    if (gotoInst->getExecSize() == g4::SIMD1)
    {   // For simd1 goto, convert it to a goto with the right execSize.
        gotoInst->setExecSize(execSize);
        gotoInst->setOptions(InstOpt_M0);
    }
    gotoInst->asCFInst()->setJip(nextJoinLabel);

    // set join's JIP to null as it will be handled later
    CFG->insertJoinToBB(joinBB, execSize, nullptr);

    if (!gotoInst->asCFInst()->isBackward() && !CFG->builder->gotoJumpOnTrue())
    {
        // For BDW/SKL, need to flip goto's pred as false condition will jump.
        G4_Predicate *pred = gotoInst->getPredicate();
        if (pred)
        {
            pred->setState(pred->getState() == PredState_Plus ? PredState_Minus : PredState_Plus);
        }
        else
        {
            // if there is no predicate, generate a predicate with all 0s.
            // if predicate is SIMD32, we have to use a :ud dst type for the move
            uint8_t numFlags = gotoInst->getExecSize() > g4::SIMD16 ? 2 : 1;
            G4_Declare* tmpFlagDcl = CFG->builder->createTempFlag(numFlags);
            G4_DstRegRegion* newPredDef = CFG->builder->createDst(tmpFlagDcl->getRegVar(), 0, 0, 1,
                numFlags == 2 ? Type_UD : Type_UW);
            G4_INST *predInst = CFG->builder->createMov(
                g4::SIMD1,
                newPredDef,
                CFG->builder->createImm(0, Type_UW),
                InstOpt_WriteEnable, false);
            INST_LIST_ITER iter = gotoBB->end();
            iter--;
            gotoBB->insertBefore(iter, predInst);

            pred = CFG->builder->createPredicate(
                PredState_Plus,
                tmpFlagDcl->getRegVar(),
                0);
            gotoInst->setPredicate(pred);
        }
    }
}

void CFGStructurizer::convertGoto(ANodeBB *node, G4_BB *nextJoinBB)
{
    G4_BB *beginbb = node->getEndBB();
    G4_INST *gotoInst = getGotoInst(beginbb);
    if (!gotoInst || node->isVisited())
    {
        return;
    }
    node->setVisited(true);

    bool isUniform = isGotoScalarJmp(gotoInst);
    G4_BB *exitbb = node->getExitBB();
    ANodeHG *innermostWhile = getInnerMostWhile(node);
    ANodeKind kind = ANKIND_GOTOJOIN;
    MUST_BE_TRUE(innermostWhile || (!innermostWhile && !node->getHasBreak()),
                 "Break isn't inside a while");
    if (innermostWhile && node->getHasBreak() &&
        innermostWhile->getKind() == ANKIND_SCF)
    {
        kind = ANKIND_SCF;
    }
    else
    {
        // nextJoinBB, if any, must be after getBeginBB(). So, if nextJoinBB
        // is before exitbb, nextJoinBB is in the middle of [beginbb, exitbb]
        bool hasJoinInMiddle = nextJoinBB && isBefore(nextJoinBB, exitbb);
        if (doScalarJmp && isUniform &&
            (gotoInst->asCFInst()->isBackward() || !hasJoinInMiddle))
        {
            kind = ANKIND_JMPI;
        }
    }
    node->setKind(kind);

    //
    // generate goto/join
    //
    G4_ExecSize execSize = gotoInst->getExecSize();
    execSize = execSize > g4::SIMD1 ? execSize : kernelExecSize;

    if (kind == ANKIND_JMPI)
    {
        CFG->convertGotoToJmpi(gotoInst);
        {
            ANodeBB *exitndbb = getANodeBB(exitbb);
            exitndbb->isJmpiTarget = true;
        }

        if (beginbb == exitbb || isBefore(exitbb, beginbb))
        {
            // TODO: Should find the 1st join BB out of this loop instead
            //       of using the next BB as join BB (see comments in
            //       convertChildren().
            G4_BB *joinbb = beginbb->getPhysicalSucc();

            // set join's JIP to null as it will be handled later
            CFG->insertJoinToBB(joinbb, execSize, nullptr);
        }
        return;
    }

    if (kind == ANKIND_SCF)
    {
        G4_BB *whilebb = innermostWhile->getEndBB();
        G4_BB *innerBlock = nextJoinBB ? nextJoinBB : whilebb;

        G4_INST* breakInst = CFG->builder->createInternalCFInst(
            gotoInst->getPredicate(), G4_break, execSize,
            innerBlock->getLabel(), whilebb->getLabel(), InstOpt_NoOpt);
        beginbb->pop_back();
        beginbb->push_back(breakInst);
        if (isUniform)
        {
            breakInst->asCFInst()->setUniform(true);
        }
        return;
    }

    G4_BB *joinbb = exitbb;
    G4_BB *JipBB = joinbb;
    if (beginbb == exitbb || isBefore(exitbb, beginbb))
    {
        joinbb = beginbb->getPhysicalSucc();
        JipBB = joinbb;
    }
    else{
        if (nextJoinBB && isBefore(nextJoinBB, JipBB))
        {
            JipBB = nextJoinBB;
        }
    }
    generateGotoJoin(beginbb, JipBB, joinbb);
}

bool CFGStructurizer::convertPST(ANode *node, G4_BB *nextJoinBB)
{
    bool change = false;
    ANodeType ty = node->getType();
    if (ty == AN_IF_THEN_ENDIF ||
        ty == AN_IF_THEN_ELSE_ENDIF ||
        ty == AN_DO_WHILE ||
        ty == AN_SEQUENCE ||
        ty == AN_COMPOSITE)
    {
        ANodeHG *nodehg = (ANodeHG*)node;
        if (node->type == AN_DO_WHILE)
        {
            // For a while loop, it must have at least one active
            // channel at exit. Thus, passing nullptr as nextJoinBB!
            convertDoWhile(nodehg, nullptr);
        }
        else if (node->type == AN_IF_THEN_ENDIF ||
                 node->type == AN_IF_THEN_ELSE_ENDIF)
        {
            convertIf(nodehg, nextJoinBB);
        }
        else
        {
            convertChildren(nodehg, nextJoinBB);
        }
        change = true;
    }
    else if (ty == AN_BB)
    {
        convertGoto((ANodeBB*)node, nextJoinBB);
        change = true;
    }
    else
    {
        MUST_BE_TRUE(false, "Wrong ANode type");
    }

    return change;
}

void CFGStructurizer::run()
{
    constructPST(CFG->begin(), CFG->end());

    if (topANodes.size() == 0)
    {
        return;
    }

    // Now, convert ANodes
    if (topANodes.size() > 0)
    {
        for (uint32_t i = 0, size = topANodes.size(); i < size; ++i)
        {
            (void)convertPST(topANodes[i], nullptr);
        }
    }

    if (anodeBBs.newBBToANodeBB.size() > 0)
    {
        // New BBs generated, reassign block Ids.
        CFG->reassignBlockIDs();
    }
}

void doCFGStructurize(FlowGraph *FG)
{
    CFGStructurizer S(FG);

    S.run();
}
