// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity active domains
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2008-2019 by Wilson Snyder.  This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3ClkGater's Transformations:
//
//   Look for clock gaters
//   Note there's overlap between this process and V3Split's
//      ALWAYS: Build graph
//          IF: Form vertex pointing to any IFs or ALWAYS above
//          VARREF: Form vertex pointing to IFs it is under
//          ASSIGN: If under normal assignment, disable optimization
//          FUTURE OPTIMIZE: If signal is set to itself, consider that OK for gating.
//          !splitable: Mark all VARREFs in this statement as comming from it
//      Optimize graph so if signal is referenced under multiple IF branches it moves up
//      Make ALWAYS for each new gating term, and move statements there
//*************************************************************************

#include "config_build.h"
#include "verilatedos.h"

#include "V3Global.h"
#include "V3Ast.h"
#include "V3Stats.h"
#include "V3Graph.h"
#include "V3ClkGater.h"

#include <algorithm>
#include <cstdarg>
#include <map>
#include <vector>

//######################################################################
// Base for debug

class GaterBaseVisitor : public AstNVisitor {
protected:
    VL_DEBUG_FUNC;  // Declare debug()
};

//######################################################################
// Support classes

class GaterVarVertex;

class GaterVertex : public V3GraphVertex {
    static uint32_t s_rankNum;
public:
    explicit GaterVertex(V3Graph* graphp)
        : V3GraphVertex(graphp) {
        s_rankNum++; rank(s_rankNum);
    }
    virtual ~GaterVertex() {}
    virtual int typeNum() const = 0;
    static void clearRank() { s_rankNum = 0; }
    virtual int sortCmp(const V3GraphVertex* rhsp) const;
};
uint32_t GaterVertex::s_rankNum = 0;

class GaterHeadVertex : public GaterVertex {
public:
    explicit GaterHeadVertex(V3Graph* graphp)
        : GaterVertex(graphp) {}
    virtual ~GaterHeadVertex() {}
    virtual int typeNum() const { return __LINE__; }  // C++ typeof() equivelent
    virtual string name() const { return "*HEAD*"; }
    virtual string dotColor() const { return "green"; }
};

class GaterPliVertex : public GaterVertex {
public:
    explicit GaterPliVertex(V3Graph* graphp)
        : GaterVertex(graphp) {}
    virtual ~GaterPliVertex() {}
    virtual int typeNum() const { return __LINE__; }  // C++ typeof() equivelent
    virtual string name() const { return "*PLI*"; }
    virtual string dotColor() const { return "red"; }
};

class GaterIfVertex : public GaterVertex {
    AstNodeIf*  m_nodep;
public:
    AstNodeIf* nodep() const { return m_nodep; }
    GaterIfVertex(V3Graph* graphp, AstNodeIf* nodep)
        : GaterVertex(graphp), m_nodep(nodep) { }
    virtual ~GaterIfVertex() {}
    virtual int typeNum() const { return __LINE__; }  // C++ typeof() equivelent
    virtual string name() const {
        return cvtToHex(m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; }
};

class GaterVarVertex : public GaterVertex {
    AstVarScope*        m_nodep;
public:
    AstVarScope* nodep() const { return m_nodep; }
    GaterVarVertex(V3Graph* graphp, AstVarScope* nodep)
        : GaterVertex(graphp), m_nodep(nodep) { }
    virtual ~GaterVarVertex() {}
    virtual int typeNum() const { return __LINE__; }  // C++ typeof() equivelent
    virtual string name() const { return nodep()->name(); }
    virtual string dotColor() const { return "skyblue"; }
};

//######################################################################
// Edge types

class GaterEdge : public V3GraphEdge {
    uint32_t m_ifelse;  // True branch of if
public:
    // These are used as shift amounts into node's user()
#define VU_DEFINE  enum VarUsage { VU_NONE=0, VU_IF=1, VU_ELSE=2, VU_PLI=4, VU_MADE=8}
    VU_DEFINE;

    uint32_t ifelse() const { return m_ifelse; }
    bool ifelseTrue()  const { return m_ifelse & VU_IF; }
    bool ifelseFalse() const { return m_ifelse & VU_ELSE; }
    bool ifelseBoth() const { return ifelseTrue() && ifelseFalse(); }
    GaterEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, uint32_t ifelse)
        : V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {}
    virtual ~GaterEdge() {}
    virtual string dotColor() const { return ((ifelse() & VU_PLI) ? "red"
                                              : (ifelseBoth() ? "blue"
                                                 : (ifelseTrue() ? "green"
                                                    : "darkgreen"))); }
    virtual int sortCmp(const V3GraphEdge* rEdgep) const {
        // Our master sort sorts by edges first, so we don't want to
        // consider the upstream vertex::sortCmp when sorting by edges.
        // We do want to order by something though; rank works
        if (fromp()->rank() < rEdgep->fromp()->rank()) return -1;
        if (fromp()->rank() > rEdgep->fromp()->rank()) return 1;
        // If same, resolve by ifelse
        const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
        if (m_ifelse < crEdgep->m_ifelse) return -1;
        if (m_ifelse > crEdgep->m_ifelse) return 1;
        return 0;
    }
};

int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const {
    const GaterVertex* crhsp = static_cast<const GaterVertex*>(rhsp);
    // We really only care about ordering Var's together, but...
    // First put same type together
    if (typeNum() < crhsp->typeNum()) return -1;
    if (typeNum() > crhsp->typeNum()) return 1;
    // If variable, group by same input fanin
    // (know they're the same type based on above compare)
    if (dynamic_cast<const GaterVarVertex*>(this)) {
        // We've already sorted by edges, so just see if same tree
        // If this gets too slow, we could compute a hash up front
        V3GraphEdge* lEdgep = this->inBeginp();
        V3GraphEdge* rEdgep = rhsp->inBeginp();
        while (lEdgep && rEdgep) {
            const GaterEdge* clEdgep = static_cast<const GaterEdge*>(lEdgep);
            const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
            if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1;
            if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1;
            if (clEdgep->ifelse() < crEdgep->ifelse()) return -1;
            if (clEdgep->ifelse() > crEdgep->ifelse()) return 1;
            lEdgep = lEdgep->inNextp();
            rEdgep = rEdgep->inNextp();
        }
        if (!lEdgep && !rEdgep) return 0;
        return lEdgep ? -1 : 1;
    }
    // Finally by rank of this vertex
    if (rank() < rhsp->rank()) return -1;
    if (rank() > rhsp->rank()) return 1;
    return 0;
}

//######################################################################
// Check for non-simple gating equations

class GaterCondVisitor : public GaterBaseVisitor {
private:
    // RETURN STATE
    bool m_isSimple;  // Set false when we know it isn't simple
    // METHODS
    inline void okIterate(AstNode* nodep) {
        if (m_isSimple) iterateChildren(nodep);
    }
    // VISITORS
    virtual void visit(AstOr* nodep) {  okIterate(nodep); }
    virtual void visit(AstAnd* nodep) { okIterate(nodep); }
    virtual void visit(AstNot* nodep) { okIterate(nodep); }
    virtual void visit(AstLogOr* nodep) {       okIterate(nodep); }
    virtual void visit(AstLogAnd* nodep) {      okIterate(nodep); }
    virtual void visit(AstLogNot* nodep) {      okIterate(nodep); }
    virtual void visit(AstVarRef* nodep) {      okIterate(nodep); }

    // Other possibilities are equals, etc
    // But, we don't want to get too complicated or it will take too much
    // effort to calculate the gater

    virtual void visit(AstNode* nodep) {
        m_isSimple = false;
        //iterateChildren(nodep);
    }
public:
    // CONSTUCTORS
    explicit GaterCondVisitor(AstNode* nodep) {
        m_isSimple = true;
        iterate(nodep);
    }
    virtual ~GaterCondVisitor() {}
    // PUBLIC METHODS
    bool isSimple() const { return m_isSimple; }
};

//######################################################################
// Check for non-simple gating equations

class GaterBodyVisitor : public GaterBaseVisitor {
    // NODE STATE
    // Input state
    //  AstVarScope::user2p()   -> AstAlways* moving this variable to

    enum State {
        // This is used as a bitmask
        STATE_UNKNOWN = 0,
        STATE_KEEP = 1,  // Seen a variable we need, keep this statement
        STATE_DELETE = 2  // Seen a variable we need, delete this statement
        // 3=keep & delete
    };

    bool        m_original;     // Deleting original statements, vs deleting new var statements
    AstNode*    m_exprp;        // New gater expression we are building
    bool        m_cloning;      // Clone this object 0=not sure yet, 1=do
    uint32_t    m_state;        // Parsing state

    // VISITORS
    virtual void visit(AstVarRef* nodep) {
        if (nodep->lvalue()) {
            AstVarScope* vscp = nodep->varScopep();
            if (vscp->user2p() == m_exprp) {
                // This variable's block needs to move to the new always
                if (m_original) {
                    UINFO(9,"  VARREF delete in old: "<<nodep<<endl);
                    m_state |= STATE_DELETE;
                } else {
                    UINFO(9,"  VARREF stays in new:  "<<nodep<<endl);
                    m_state |= STATE_KEEP;
                }
            } else {
                if (m_original) {
                    UINFO(9,"  VARREF other stays in old\n");
                    m_state |= STATE_KEEP;
                } else {
                    UINFO(9,"  VARREF other delete in new\n");
                    m_state |= STATE_DELETE;
                }
            }
        }
    }

    //virtual void visit(AstNodeIf* nodep) { ... }
    // Not needed, it's the same handling as any other statement.  Cool, huh?
    // (We may get empty IFs but the constant propagater will rip them up for us)

    virtual void visit(AstNodeStmt* nodep) {
        if (!nodep->isStatement()) {
            iterateChildren(nodep);
            return;
        }
        uint32_t oldstate = m_state;
        // Find if children want to delete this or not.
        // Note children may bicker, and want to both keep and delete (branches on a if)
        uint32_t childstate;
        {
            m_state = STATE_UNKNOWN;
            iterateChildren(nodep);
            childstate = m_state;
        }
        m_state = oldstate;
        UINFO(9,"    Did state="<<childstate<<"  "<<nodep<<endl);

        // Indeterminate (statement with no lvalue), so keep only in original
        if (childstate == STATE_UNKNOWN) {
            if (m_original) childstate |= STATE_KEEP;
            else childstate |= STATE_DELETE;
        }

        if ((childstate & STATE_DELETE) && !(childstate & STATE_KEEP)) {
            nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
            // Pass upwards we did delete
            m_state |= STATE_DELETE;
        } else {
            // Pass upwards we must keep
            m_state |= STATE_KEEP;
        }
    }
    virtual void visit(AstNode* nodep) {
        iterateChildren(nodep);
    }
public:
    // CONSTUCTORS
    GaterBodyVisitor(AstAlways* nodep, AstNode* exprp, bool original) {
        m_exprp = exprp;
        m_original = original;
        m_state = STATE_UNKNOWN;
        m_cloning = false;
        if (debug()>=9) nodep->dumpTree(cout, "  GateBodyIn:  ");
        iterateAndNextNull(nodep->bodysp());
        if (debug()>=9) nodep->dumpTree(cout, "  GateBodyOut: ");
        // If there's no statements we shouldn't have had a resulting graph
        // vertex asking for this creation
    }
    virtual ~GaterBodyVisitor() {}
};

//######################################################################
// Create clock gaters

class GaterVisitor : public GaterBaseVisitor {
    // NODE STATE
    // Cleared on Always
    //  AstVarScope::user1p()   -> GaterVarVertex*
    //  AstAlways::user4()      -> bool.  True indicates processed
    // Cleared on each new Always
    //  AstVarScope::user2p()   -> AstAlways* moving this variable to
    AstUser1InUse       m_inuser1;
    AstUser2InUse       m_inuser2;
    AstUser4InUse       m_inuser4;

    // GRAPH STATE
    // Cleared on simplify
    //  Vertex::user()          -> VarUsage: Mark of which if/else edges are hit

    // TYPES
    VU_DEFINE;

    enum MiscConsts {
        IF_DEPTH_MAX = 4,       // IFs deep we bother to analyze
        DOMAINS_MAX = 32        // Clock domains before avoiding O(N^2) blowup
    };

    // MEMBERS
    string      m_nonopt;               // Reason block is not optimizable
    V3Double0   m_statGaters;           // Statistic tracking
    V3Double0   m_statBits;             // Statistic tracking
    bool        m_directlyUnderAlw;     // Immediately under Always or If
    int         m_ifDepth;              // Depth of IF statements
    int         m_numIfs;               // Number of IF statements
    V3Graph     m_graph;                // Scoreboard of var usages/dependencies
    GaterPliVertex*     m_pliVertexp;   // Element specifying PLI ordering
    GaterHeadVertex*    m_headVertexp;  // Top vertex
    V3GraphVertex*      m_aboveVertexp; // Vertex above this point in tree
    uint32_t            m_aboveTrue;    // Vertex above this point is true branch
    AstVarScope*        m_stmtVscp;     // Current statement had variable assigned
    bool                m_stmtInPli;    // Current statement has PLI

    // METHODS
    void nonOptimizable(AstNode* nodep, const char* reasonp) {
        if (m_nonopt=="") {
            UINFO(9,"    Nonopt: "<<reasonp<<": "<<nodep<<endl);
            m_nonopt = reasonp;
        }
    }
    void scoreboardPli(AstNode* nodep) {
        // Order all PLI statements with other PLI statements
        // This ensures $display's and such remain in proper order
        // We don't prevent splitting out other non-pli statements, however,
        // because it is common to have $uasserts sprinkled about.
        if (!m_pliVertexp) {
            m_pliVertexp = new GaterPliVertex(&m_graph);
        }
        if (m_stmtVscp) {  // Already saw a variable, be sure to mark it!
            GaterVarVertex* varVtxp = reinterpret_cast<GaterVarVertex*>(m_stmtVscp->user1p());
            new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI);
        }
        m_stmtInPli = true;  // Mark all followon variables too
    }

    // METHODS -- GRAPH stuff
    void simplifyGraph() {
        if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false);

        // We now have all PLI variables with edges FROM the pli vertex
        simplifyPli();
        if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false);

        // Any vertex that points along both true & false path to variable
        // can be simplfied so parent points to that vertex.  Any vertex
        // that points to a (great...) grandparent of a variable can just
        // point to the edge.
        m_graph.userClearVertices();  // user() will contain VarUsage
        simplifyIfElseRecurse(m_headVertexp);
        if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false);

        m_graph.userClearVertices();  // user() will contain VarUsage
        simplifyGrandRecurse(m_headVertexp, 1);
        if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false);

        simplifyRemoveUngated(m_headVertexp);

        // Give all signals with same gating term same color
        graphColorSameFeeder();

        if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false);
    }

    void simplifyPli() {
        // For now, we'll not gate any logic with PLI lvariables in it.  In
        // the future we may move PLI statements.  One way to do so is to
        // all below pli VARs to go IfVertex -> PliVertex -> VarVertex then
        // they all must get colored the same. There may be a lot of
        // duplicated edges to the PLI; they'll need cleanup.  All of the
        // later optimizations need to deal, and the GaterBodyVisitor needs
        // to know how to move them.
        if (m_pliVertexp) {
            // Follow PLI out edges to find all relevant variables
            for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) {
                nextp = edgep->outNextp();  // We may edit the list
                if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
                    vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep);
                } else {
                    m_graph.dump();
                    v3fatalSrc("PLI vertex points to non-signal");
                }
            }
            m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL;
        }
    }

    void simplifyIfElseRecurse(V3GraphVertex* vertexp) {
        // From bottom-up, propagate duplicate IF/ELSE branches to grandparent
        for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
            simplifyIfElseRecurse(edgep->top());
        }
        //UINFO(9,"IERecurse "<<vertexp<<endl);
        if (dynamic_cast<GaterIfVertex*>(vertexp)) {
            // Clear indications
            for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
                edgep->top()->user(VU_NONE);
            }
            // Mark nodes on to/from side
            for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
                V3GraphVertex* toVxp = edgep->top();
                GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
                // We may mark this twice in one pass - if so it's duplicated; no worries
                if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF);
                if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE);
                //UINFO(9,"  mark "<<edgep<<" "<<toVxp<<endl);
            }
            // Any with both IF & ELSE mark get removal mark, and new parent edge
            for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
                nextp = edgep->outNextp();  // We may edit the list
                V3GraphVertex* toVxp = edgep->top();
                //UINFO(9," to "<<toVxp->user()<<" "<<toVxp<<endl);
                if ((toVxp->user() & VU_IF) && (toVxp->user() & VU_ELSE)) {
                    edgep->unlinkDelete(); VL_DANGLING(edgep);
                    if (!(toVxp->user() & VU_MADE)) {  // Make an edge only once
                        toVxp->user(toVxp->user() | VU_MADE);
                        GaterEdge* inedgep = static_cast<GaterEdge*>(vertexp->inBeginp());
                        V3GraphVertex* grandparent = inedgep->fromp();
                        new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse());
                    }
                }
            }
        }
    }

    void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) {
        // From top-down delete any vars that grandparents source
        // IE    A -> B -> C -> VAR
        //         \----------^
        //UINFO(9,"GRecurse "<<depth<<" "<<vertexp<<endl);
        // Mark all variables to be swept
        for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
            nextp = edgep->outNextp();  // We may edit the list
            if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
                if (toVxp->user() && toVxp->user() < depth) {
                    // A recursion "above" us marked it,
                    // Remove this edge, it's redundant with the upper edge
                    edgep->unlinkDelete(); VL_DANGLING(edgep);
                } else {
                    GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
                    if (cedgep->ifelseBoth()) {
                        toVxp->user(depth);
                    }
                }
            }
        }
        // Recurse
        for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
            simplifyGrandRecurse(edgep->top(), depth+1);
        }
        // Clean our marks
        for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
            V3GraphVertex* toVxp = edgep->top();
            if (toVxp->user() && toVxp->user() < depth) {  // A recursion "above" us marked it; don't mess with it
            } else {
                toVxp->user(0);  // We marked it originally, so unmark now
            }
        }
        // Delete any If nodes with no children
        // Last, as want bottom-up cleanup
        for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
            nextp = edgep->outNextp();  // We may edit the list
            if (GaterIfVertex* toVxp = dynamic_cast<GaterIfVertex*>(edgep->top())) {
                if (!toVxp->outBeginp()) {
                    if (!nextp || nextp->top() != edgep->top()) {  // Else next would disappear; we'll do it next loop
                        toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
                    }
                }
            }
        }
    }

    void simplifyRemoveUngated(V3GraphVertex* vertexp) {
        // Remove variables that are ungated
        // At this point, any variable under the head is ungated
        for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
            nextp = edgep->outNextp();  // We may edit the list
            if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
                if (!nextp || nextp->top() != edgep->top()) {  // Else next would disappear; we'll do it next loop
                    toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep);
                }
            }
        }
    }

    void graphColorSameFeeder() {
        // Assign same color to all destination vertices that have same
        // subgraph feeding into them
        // (I.E. all "from" nodes are common within each color)
        // We could hash, but instead it's faster to sort edges, so we know
        // the same edge list is always adjacent.  The result is a
        // O(vertices*edges) loop, but we'd need that to hash too.
        if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); }

        m_graph.sortEdges();

        // Now sort vertices, so same inbound edge set ends up adjacent
        m_graph.sortVertices();

        // Now walk and assign colors; same color is adjacent
        m_graph.clearColors();
        m_graph.userClearEdges();  // Used by newExprFromGraph
        uint32_t color = 1;
        GaterVarVertex* lastVxp = NULL;
        for (V3GraphVertex* vertexp = m_graph.verticesBeginp();
             vertexp; vertexp=vertexp->verticesNextp()) {
            if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
                if (!vVxp->inBeginp()) {
                    // At this point, any variable not linked is an error
                    // (It should have at least landed under the Head node)
                    vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection");
                }
                if (!lastVxp || vVxp->sortCmp(lastVxp)) {
                    // Different sources for this new node
                    color++;
                }
                vVxp->color(color);
                lastVxp = vVxp;
            }
        }

        if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); }
    }

    AstNode* newExprFromGraph(GaterVarVertex* vertexp) {
        // Recurse backwards, then form equation on return path
        // We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation
        // We get equations like "a | (!a & b)" which obviously could be reduced here,
        // instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up
        static uint32_t s_generation = 0;
        ++s_generation;
        nafgMarkRecurse(vertexp, s_generation);
        AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation);
        if (debug()>=9) nodep->dumpTree(cout, "  GateExpr: ");
        return nodep;
    }
    void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) {
        // Backwards mark user() on the path we recurse
        //UINFO(9," nafgMark: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
        for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
            //UINFO(9," nafgMark: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
            edgep->user(generation);
            nafgMarkRecurse(edgep->fromp(), generation);
        }
    }
    AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) {
        // Forewards follow user() marked previously and build tree
        AstNode* nodep = NULL;
        // OR across all edges found at this level
        //UINFO(9," nafgEnter: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
        for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
            if (edgep->user() == generation) {
                GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
                AstNode* eqnp = NULL;
                //UINFO(9," nafgFollow: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
                if (dynamic_cast<GaterHeadVertex*>(edgep->fromp())) {
                    // Just OR in all lower terms
                    eqnp = nafgCreateRecurse(edgep->top(), generation);
                } else if (GaterIfVertex* cVxp = dynamic_cast<GaterIfVertex*>(edgep->fromp())) {
                    // Edges from IFs represent a real IF branch in the equation tree
                    //UINFO(9,"  ifver "<<cvtToHex(edgep)<<" cc"<<edgep->dotColor()<<endl);
                    eqnp = cVxp->nodep()->condp()->cloneTree(true);
                    if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition");
                    if (cedgep->ifelseFalse()) {
                        eqnp = new AstNot(eqnp->fileline(), eqnp);
                    }
                    // We need to AND this term onto whatever was found below it
                    AstNode* belowp = nafgCreateRecurse(edgep->top(), generation);
                    if (belowp) eqnp = new AstAnd(eqnp->fileline(), eqnp, belowp);
                }
                // Top level we could choose to make multiple gaters, or ORs under the gater
                // Right now we'll put OR lower down and let other optimizations deal
                if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp);
                else nodep = eqnp;
                //if (debug()>=9) nodep->dumpTree(cout, "      followExpr: ");
            }
        }
        //UINFO(9," nafgExit:  "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
        return nodep;
    }

    void newAlwaysTrees(AstAlways* nodep) {
        // Across all variables we're moving
        uint32_t lastColor = 0;
        AstNode* lastExprp = NULL;
        for (V3GraphVertex* vertexp = m_graph.verticesBeginp();
             vertexp; vertexp=vertexp->verticesNextp()) {
            if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
                if (!lastExprp || lastColor != vVxp->color()) {
                    lastColor = vVxp->color();
                    // Create the block we've just finished
                    if (lastExprp) newAlwaysTree(nodep, lastExprp);  // Duplicate below
                    // New expression for this color
                    lastExprp = newExprFromGraph(vVxp);
                }
                // Mark variable to move
                if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters");
                vVxp->nodep()->user2p(lastExprp);
                m_statBits += vVxp->nodep()->width();  // Moving a wide bus counts more!
                // There shouldn't be two possibilities we want to
                // move to, IE {A,B} <= Z because we marked such
                // things as unoptimizable
            }
        }
        // Create the final block we've just finished
        if (lastExprp) newAlwaysTree(nodep, lastExprp);  // Duplicate above
    }

    void newAlwaysTree(AstAlways* nodep, AstNode* exprp) {
        // Create new always with specified gating expression
        // Relevant vars are already marked with user2p

        // Should we put the gater under the always itself,
        // or make a new signal?
        //   New signal: May cause replicated logic until we can combine
        //              Need way to toggle signal on complicated pos/negedge
        //   Under Always: More complicated, may not propagate logic with gateer
        //      Untill we get better gate optimization, we'll go this route.
//#define GATER_NOP
#ifdef GATER_NOP
        // For testing only, don't clock gate but still make new always
        AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
#else
        // Make a SenGate
        AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
        if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");

        AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
                                             new AstSenGate(nodep->fileline(),
                                                            oldsenitemsp->cloneTree(true),
                                                            exprp));
#endif

        // Make new body; note clone will preserve user2p() indicating moving vars
        AstNode* bodyp = nodep->bodysp()->cloneTree(true);

        AstAlways* alwp = new AstAlways(nodep->fileline(),
                                        nodep->keyword(),
                                        sensesp,
                                        bodyp);

        alwp->user4(1);  // No need to process the new always again!
        nodep->addNextHere(alwp);

        // Blow moved statements from old body
        { GaterBodyVisitor vis(nodep, exprp, true); }
        // Blow old statements from new body
        { GaterBodyVisitor vis(alwp, exprp, false); }

        ++m_statGaters;
        if (debug()>=9) alwp->dumpTree(cout, "  new: ");
    }

    // VISITORS
    virtual void visit(AstAlways* nodep) {
        if (debug()>=9) cout<<endl<<endl<<endl;
        UINFO(5, "Gater: ALWAYS: "<<nodep<<endl);
        if (nodep->user4SetOnce()) return;

        clear();

        if (debug()>=9) nodep->dumpTree(cout, "  Alwin:  ");

        // Look for constructs we can't optimize
        // Form graph with Vertices at each IF, and each Variable
        m_aboveTrue = VU_IF | VU_ELSE;
        iterateChildrenAlw(nodep, true);

        // Other reasons to not optimize
        if (!m_numIfs) nonOptimizable(nodep, "No if statements");

        // Something to optimize, oh my!
        if (m_nonopt!="") {
            UINFO(5, "  Gater non-opt: "<<m_nonopt<<endl);
        } else {
            // Process it
            simplifyGraph();

            // See how much moves
            uint32_t lastColor = 0;
            for (V3GraphVertex* vertexp = m_graph.verticesBeginp();
                 vertexp; vertexp=vertexp->verticesNextp()) {
                if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) {
                    if (lastColor < vVxp->color()) {
                        lastColor = vVxp->color();
                    }
                }
            }
            if (lastColor == 0) {  // Nothing we moved!
                nonOptimizable(nodep, "Nothing moved");
            }
            else if (lastColor > DOMAINS_MAX) {
                // Our move algorithm is fairly slow and if we're splitting
                // up too much it'll get really nasty.  It's probably a bad
                // move for performance to split too much, anyhow, as the
                // number of gaters will result in calling many small c functions.
                nonOptimizable(nodep, "Too much moved");
            }
            if (m_nonopt=="") {
                newAlwaysTrees(nodep);
                if (debug()>=9) nodep->dumpTree(cout, "  Gaterout: ");
            }
        }
        UINFO(5, "  Gater done"<<endl);
    }
    virtual void visit(AstVarRef* nodep) {
        if (nodep->lvalue()) {
            AstVarScope* vscp = nodep->varScopep();
            if (nodep->varp()->isSigPublic()) {
                // Public signals shouldn't be changed, pli code might be messing with them
                scoreboardPli(nodep);
            }
            // If another lvalue in this node, give up optimizing.
            // We could just not optimize this variable, but we've already marked the
            // other variable as optimizable, so we can instead pretend it's a PLI node.
            if (m_stmtVscp) {
                UINFO(5, "   Multiple lvalues in one statement: "<<nodep<<endl);
                scoreboardPli(nodep);  // This will set m_stmtInPli
            }
            m_stmtVscp = vscp;
            // Find, or make new Vertex
            GaterVarVertex* vertexp = reinterpret_cast<GaterVarVertex*>(vscp->user1p());
            if (!vertexp) {
                vertexp = new GaterVarVertex(&m_graph, vscp);
                vscp->user1p(vertexp);
            }
            new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
            if (m_stmtInPli) {
                new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI);
            }
        }
    }
    virtual void visit(AstNodeIf* nodep) {
        m_ifDepth++;
        bool allowGater = m_directlyUnderAlw && m_ifDepth <= IF_DEPTH_MAX;
        if (allowGater) {
            GaterCondVisitor condVisitor(nodep->condp());
            if (!condVisitor.isSimple()) {
                // Don't clear optimization, simply drop this IF as part of the gating
                UINFO(5,"   IFnon-simple-condition: "<<nodep<<endl);
                allowGater = false;
            }
        }
        if (!allowGater) {
            // IF burried under something complicated
            iterateChildrenAlw(nodep, true);
        } else {
            UINFO(5, "  IF: "<<nodep<<endl);
            m_numIfs++;
            // Recurse to next level of if.
            V3GraphVertex*      lastabovep = m_aboveVertexp;
            uint32_t            lasttrue = m_aboveTrue;
            GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
            new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
            {
                iterateAndNextNull(nodep->condp());  // directlyUnder stays as-is
            }
            {
                m_aboveVertexp = vertexp;  // Vars will point at this edge
                m_aboveTrue = VU_IF;
                iterateAndNextNull(nodep->ifsp());  // directlyUnder stays as-is (true)
            }
            {
                m_aboveVertexp = vertexp;  // Vars will point at this edge
                m_aboveTrue = VU_ELSE;
                iterateAndNextNull(nodep->elsesp());  // directlyUnder stays as-is (true)
            }
            m_aboveVertexp = lastabovep;
            m_aboveTrue = lasttrue;
        }
        m_ifDepth--;
    }

    virtual void visit(AstAssignDly* nodep) {
        // iterateChildrenAlw will detect this is a statement for us
        iterateChildrenAlw(nodep, false);
    }

    virtual void visit(AstNodeAssign* nodep) {
        // Note NOT AssignDly; handled above, We'll just mark this block as
        // not optimizable.
        //
        // A future alternative is to look for any lvalues that are also
        // variables in the sensitivity list, or rvalues in this block.  If
        // any hit, disable optimization.  Unlikely to be useful (For loops
        // being an exception, but they're already unrolled.)
        nonOptimizable(nodep, "Non-delayed assignment");
        // No reason to iterate.
    }

    virtual void visit(AstSenItem* nodep) {
        if (!nodep->isClocked()) {
            nonOptimizable(nodep, "Non-clocked sensitivity");
        }
        iterateChildrenAlw(nodep, false);
    }

    //--------------------
    virtual void visit(AstNode* nodep) {
        if (m_nonopt=="") {  // Else accelerate
            iterateChildrenAlw(nodep, false);
        }
    }
    inline void iterateChildrenAlw(AstNode* nodep, bool under) {
        // **** USE THIS INSTEAD OF iterateChildren!
        // Note If visitor doesn't call here; does it its own way
        bool lastdua = m_directlyUnderAlw;
        AstVarScope* lastvscp = m_stmtVscp;
        bool lastpli = m_stmtInPli;
        m_directlyUnderAlw = under;
        if (VN_IS(nodep, NodeStmt)) {  // Restored below
            UINFO(9,"       Stmt: "<<nodep<<endl);
            m_stmtVscp = NULL;
            m_stmtInPli = false;
        }
        if (!nodep->isPure() || nodep->isBrancher()) {
            // May also be a new statement (above if); if so we mark it immediately
            UINFO(9,"         NotPure "<<nodep<<endl);
            scoreboardPli(nodep);
        }
        {
            iterateChildren(nodep);
        }
        m_directlyUnderAlw = lastdua;
        if (VN_IS(nodep, NodeStmt)) {  // Reset what set above; else propagate up to above statement
            m_stmtVscp = lastvscp;
            m_stmtInPli = lastpli;
        }
    }

public:
    // CONSTUCTORS
    explicit GaterVisitor(AstNetlist* nodep) {
        // AstAlways visitor does the real work, so most zeroing needs to be in clear()
        clear();
        iterate(nodep);
    }
    void clear() {
        m_nonopt = "";
        m_directlyUnderAlw = false;
        m_ifDepth = 0;
        m_numIfs = 0;
        AstNode::user1ClearTree();
        AstNode::user2ClearTree();
        // Prepare graph
        m_graph.clear();
        GaterVertex::clearRank();
        m_pliVertexp = NULL;
        m_headVertexp = new GaterHeadVertex(&m_graph);
        m_aboveVertexp = m_headVertexp;
        m_aboveTrue = false;
        m_stmtVscp = NULL;
        m_stmtInPli = false;
    }
    virtual ~GaterVisitor() {
        V3Stats::addStat("Optimizations, Gaters inserted", m_statGaters);
        V3Stats::addStat("Optimizations, Gaters impacted bits", m_statBits);
    }
};

//######################################################################
// Active class functions

void V3ClkGater::clkGaterAll(AstNetlist* nodep) {
    UINFO(2,__FUNCTION__<<": "<<endl);
    {
        // While the gater does well at some modules, it seems to slow down many others
        UINFO(5,"ClkGater is disabled due to performance issues\n");
    }  // Destruct before checking
    //GaterVisitor visitor (nodep);
    V3Global::dumpCheckGlobalTree("clkgater", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}
