// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: DfgVertex sub-classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// 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.
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// This is a data-flow graph based representation of combinational logic,
// the main difference from a V3Graph is that DfgVertex owns the storage
// of it's input edges (operands/sources/arguments), and can access each
// input edge directly by indexing, making modifications more efficient
// than the linked list based structures used by V3Graph.
//
// A bulk of the DfgVertex sub-types are generated by astgen, and are
// analogous to the corresponding AstNode sub-types.
//
// See also the internals documentation docs/internals.rst
//
//*************************************************************************

#ifndef VERILATOR_V3DFGVERTICES_H_
#define VERILATOR_V3DFGVERTICES_H_

#ifndef VERILATOR_V3DFG_H_
#error "Use V3Dfg.h as the include"
#include "V3Dfg.h"  // This helps code analysis tools pick up symbols in V3Dfg.h
#define VL_NOT_FINAL  // This #define fixes broken code folding in the CLion IDE
#endif

// Include macros generated by 'astgen'. These include DFGGEN_MEMBERS_<Node>
// for each DfgVertex sub-type. The generated members include boilerplate
// methods related to cloning, visitor dispatch, and other functionality.
// For precise details please read the generated macros.
#include "V3Dfg__gen_macros.h"

//------------------------------------------------------------------------------
// Variable vertices - represent a variables

class DfgVertexVar VL_NOT_FINAL : public DfgVertex {
    // Represents a variable. It has 2 optional inputs, 'srcp' and 'defaultp'.

    AstVarScope* const m_vscp;  // The AstVarScope associated with this vertex (not owned)
    // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
    FileLine* m_driverFileLine = nullptr;
    // If this DfgVertexVar is a synthesized temporary, this is the original AstVarScope it stands
    // for. It might point to m_vscp itself to indicate it's a temporary without an associated
    // input AstVarScope.
    AstVarScope* m_tmpForp = nullptr;

protected:
    DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
        : DfgVertex{dfg, type, vscp->varp()->fileline(),
                    *DfgDataType::fromAst(vscp->varp()->dtypep())}
        , m_vscp{vscp} {
        // Increment reference count
        m_vscp->user1(m_vscp->user1() + 0x20);
        UASSERT_OBJ((m_vscp->user1() >> 5) > 0, m_vscp, "Reference count overflow");
        // Allocate sources
        newInput();
        newInput();
    }

public:
    ~DfgVertexVar() {
        // Decrement reference count
        m_vscp->user1(m_vscp->user1() - 0x20);
        UASSERT_OBJ((m_vscp->user1() >> 5) >= 0, m_vscp, "Reference count underflow");
    }
    ASTGEN_MEMBERS_DfgVertexVar;

    // The driver of the variable. Might be nullptr if driven exernally (input to DfgGraph).
    DfgVertex* srcp() const { return inputp(0); }
    void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
    // The default value of the variable. This defines the parts not driven by 'srcp', maybe null
    DfgVertex* defaultp() const { return inputp(1); }
    void defaultp(DfgVertex* vtxp) { inputp(1, vtxp); }

    std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }

    // The Ast variable this vertex representess
    // AstVar* varp() const { return m_varp; }
    AstVarScope* vscp() const { return m_vscp; }

    // If this is a temporary, the Ast variable it stands for,  or same as
    // 'nodep()' if it's a temporary with no associated original Ast variable.
    AstVarScope* tmpForp() const { return m_tmpForp; }
    void tmpForp(AstVarScope* nodep) { m_tmpForp = nodep; }

    // Location of driver of variable (only used if 'srcp' is not a splice)
    FileLine* driverFileLine() const { return m_driverFileLine; }
    void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }

    // Variable referenced from other DFG in the same module/netlist
    bool hasDfgRefs() const { return m_vscp->user1() >> 6; }  // I.e.: (nodep()->user1() >> 5) > 1

    // Variable referenced from Ast code in the same module/netlist
    static bool hasModWrRefs(const AstVarScope* nodep) { return nodep->user1() & 0x08; }
    static void setHasModWrRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x08); }
    bool hasModWrRefs() const { return hasModWrRefs(m_vscp); }
    void setHasModWrRefs() const { setHasModWrRefs(m_vscp); }

    // Variable referenced outside the containing module/netlist.
    static bool hasExtRdRefs(const AstVarScope* nodep) { return nodep->user1() & 0x01; }
    static bool hasExtWrRefs(const AstVarScope* nodep) { return nodep->user1() & 0x02; }
    static void setHasExtRdRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x01); }
    static void setHasExtWrRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x02); }
    bool hasExtRdRefs() const { return hasExtRdRefs(m_vscp); }
    bool hasExtWrRefs() const { return hasExtWrRefs(m_vscp); }
    bool hasExtRefs() const { return hasExtRdRefs() || hasExtWrRefs(); }

    // Variable referenced via READWRITE references
    static bool hasRWRefs(const AstVarScope* nodep) { return nodep->user1() & 0x10; }
    static void setHasRWRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x10); }

    // True iff the value of this variable is read outside this DfgGraph
    bool isObserved() const {
        // A DfgVarVertex is written in exactly one DfgGraph, and might be read in an arbitrary
        // number of other DfgGraphs. If it's driven in this DfgGraph, it's read in others.
        if (hasDfgRefs()) return srcp() || defaultp();
        return hasExtRdRefs();
    }

    // The value of this vertex might differ from what is defined by its drivers
    // 'srcp' and 'defaultp'. That is, it might be assigned, possibly partially,
    // or abruptly outside the graph, hence it is not equivalent to its 'srcp'.
    static bool isVolatile(const AstVarScope* nodep) {
        return hasModWrRefs(nodep) || hasExtWrRefs(nodep);
    }
    bool isVolatile() const { return isVolatile(m_vscp); }
};

class DfgVarArray final : public DfgVertexVar {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgVarArray(DfgGraph& dfg, AstVarScope* vscp)
        : DfgVertexVar{dfg, dfgType(), vscp} {
        UASSERT_OBJ(isArray(), vscp, "Non-array DfgVarArray");
    }
    ASTGEN_MEMBERS_DfgVarArray;
};

class DfgVarPacked final : public DfgVertexVar {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
        : DfgVertexVar{dfg, dfgType(), vscp} {
        UASSERT_OBJ(isPacked(), vscp, "Non-packed DfgVarPacked");
    }
    ASTGEN_MEMBERS_DfgVarPacked;
};

//------------------------------------------------------------------------------
// Ast reference vertices

class DfgVertexAst VL_NOT_FINAL : public DfgVertex {
    // Represents a reference in the Ast

    AstNodeExpr* m_exprp;  // The AstNodeExpr representing this reference

public:
    DfgVertexAst(DfgGraph& dfg, VDfgType type, AstNodeExpr* exprp)
        : DfgVertex{dfg, type, exprp->fileline(), *DfgDataType::fromAst(exprp->dtypep())}
        , m_exprp{exprp} {}
    ASTGEN_MEMBERS_DfgVertexAst;

    AstNodeExpr* exprp() const { return m_exprp; }
    void exprp(AstNodeExpr* exprp) { m_exprp = exprp; }
};

class DfgAstRd final : public DfgVertexAst {
    friend class DfgVertex;
    friend class DfgVisitor;

    const bool m_inSenItem;  // Reference is in a sensitivity list
    const bool m_inLoop;  // Reference is in a loop

public:
    DfgAstRd(DfgGraph& dfg, AstNodeExpr* exprp, bool inSenItem, bool inLoop)
        : DfgVertexAst{dfg, dfgType(), exprp}
        , m_inSenItem{inSenItem}
        , m_inLoop{inLoop} {
        // Allocate sources
        newInput();
    }
    ASTGEN_MEMBERS_DfgAstRd;

    DfgVertex* srcp() const { return inputp(0); }
    void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
    std::string srcName(size_t) const override final { return ""; }
    bool inSenItem() const { return m_inSenItem; }
    bool inLoop() const { return m_inLoop; }
};

//------------------------------------------------------------------------------
// Nullary vertices - 0 inputs

class DfgVertexNullary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexNullary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {}

public:
    ASTGEN_MEMBERS_DfgVertexNullary;
    std::string srcName(size_t) const override final { V3ERROR_NA_RETURN(""); }
};

class DfgConst final : public DfgVertexNullary {
    friend class DfgVertex;
    friend class DfgVisitor;

    V3Number m_num;  // Constant value

public:
    DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num)
        : DfgVertexNullary{dfg, dfgType(), flp, DfgDataType::packed(num.width())}
        , m_num{num} {}
    DfgConst(DfgGraph& dfg, FileLine* flp, size_t width, uint32_t value)
        : DfgVertexNullary{dfg, dfgType(), flp, DfgDataType::packed(width)}
        , m_num{flp, static_cast<int>(width), value} {}

    ASTGEN_MEMBERS_DfgConst;

    V3Number& num() { return m_num; }
    const V3Number& num() const { return m_num; }

    size_t toSizeT() const { return static_cast<size_t>(num().toUQuad()); }
    uint32_t toU32() const { return num().toUInt(); }

    bool isZero() const { return num().isEqZero(); }
    bool isOnes() const { return num().isEqAllOnes(width()); }

    // Does this DfgConst have the given value? Note this is not easy to answer if wider than 32.
    bool hasValue(uint32_t value) const {
        return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32;
    }
};

//------------------------------------------------------------------------------
// Unary vertices - 1 inputs

class DfgVertexUnary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexUnary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {
        newInput();
    }

public:
    ASTGEN_MEMBERS_DfgVertexUnary;
    DfgVertex* srcp() const { return inputp(0); }
    void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
    std::string srcName(size_t) const override final { return ""; }
};

class DfgRep final : public DfgVertexUnary {
    // AstReplicate is binary, but 'countp' is always constant. Represented in
    // Dfg as a unary vertex, with the count determined by the data type,
    // similar to Extend/ExtendS.
public:
    DfgRep(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexUnary{dfg, dfgType(), flp, dtype} {}
    ASTGEN_MEMBERS_DfgRep;
    uint32_t count() const { return width() / srcp()->width(); }
};

class DfgSel final : public DfgVertexUnary {
    // AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
    // common, we special case as a DfgSel for the constant 'lsbp', and as
    // 'DfgMux` for the non-constant 'lsbp'.
    uint32_t m_lsb = 0;  // The LSB index

public:
    DfgSel(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexUnary{dfg, dfgType(), flp, dtype} {}
    ASTGEN_MEMBERS_DfgSel;

    DfgVertex* fromp() const { return srcp(); }
    void fromp(DfgVertex* vtxp) { srcp(vtxp); }
    uint32_t lsb() const { return m_lsb; }
    void lsb(uint32_t value) { m_lsb = value; }
};

class DfgUnitArray final : public DfgVertexUnary {
    // This is a type adapter for modeling arrays. It's a single element array,
    // with the value of the single element being the source operand.
public:
    DfgUnitArray(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexUnary{dfg, dfgType(), flp, dtype} {
        UASSERT_OBJ(isArray(), flp, "Non-array DfgUnitArray");
        UASSERT_OBJ(size() == 1, flp, "DfgUnitArray must have a single element");
    }
    ASTGEN_MEMBERS_DfgUnitArray;
};

//------------------------------------------------------------------------------
// Binary vertices - 2 inputs

class DfgVertexBinary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexBinary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {
        newInput();
        newInput();
    }

public:
    ASTGEN_MEMBERS_DfgVertexBinary;
};

class DfgMux final : public DfgVertexBinary {
    // AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
    // common, we special case as a DfgSel for the constant 'lsbp', and as
    // 'DfgMux` for the non-constant 'lsbp'.
public:
    DfgMux(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexBinary{dfg, dfgType(), flp, dtype} {}
    ASTGEN_MEMBERS_DfgMux;

    DfgVertex* fromp() const { return inputp(0); }
    void fromp(DfgVertex* vtxp) { inputp(0, vtxp); }
    DfgVertex* lsbp() const { return inputp(1); }
    void lsbp(DfgVertex* vtxp) { inputp(1, vtxp); }

    std::string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; }
};

//------------------------------------------------------------------------------
// Ternary vertices - 3 inputs

class DfgVertexTernary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexTernary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {
        newInput();
        newInput();
        newInput();
    }

public:
    ASTGEN_MEMBERS_DfgVertexTernary;
};

//------------------------------------------------------------------------------
// Variadic vertices - variable number of inputs

class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {}

public:
    ASTGEN_MEMBERS_DfgVertexVariadic;
};

class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
    // Represents a partial update to a varibale

    struct DriverData final {
        uint32_t m_lo;  // Low index of range driven by this driver
        FileLine* m_flp;  // Location of this driver
        DriverData() = delete;
        DriverData(uint32_t lo, FileLine* flp)
            : m_lo{lo}
            , m_flp{flp} {}
    };
    std::vector<DriverData> m_driverData;  // Additional data associated with each driver

protected:
    DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexVariadic{dfg, type, flp, dtype} {}

public:
    ASTGEN_MEMBERS_DfgVertexSplice;

    // Add driver
    void addDriver(DfgVertex* vtxp, uint32_t lo, FileLine* flp) {
        UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "addDriver called with DfgLogic");
        m_driverData.emplace_back(lo, flp);
        newInput()->relinkSrcp(vtxp);
    }

    void resetDrivers() {
        resetInputs();
        m_driverData.clear();
    }

    std::string srcName(size_t idx) const override final {
        const uint32_t lo = m_driverData[idx].m_lo;
        const uint32_t hi = lo + inputp(idx)->size() - 1;
        return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']';
    }

    FileLine* driverFileLine(size_t idx) const { return m_driverData.at(idx).m_flp; }

    DfgVertex* driverAt(size_t idx) const {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (m_driverData[i].m_lo == idx) return inputp(i);
        }
        return nullptr;
    }

    // If drives the whole result explicitly (not through defaultp), this is
    // the actual driver this DfgVertexSplice can be replaced with.
    DfgVertex* wholep() {
        if (nInputs() != 1) return nullptr;
        if (m_driverData[0].m_lo != 0) return nullptr;
        DfgVertex* const vtxp = inputp(0);
        if (vtxp->size() != size()) return nullptr;
        if (const DfgUnitArray* const uap = vtxp->cast<DfgUnitArray>()) {
            if (DfgVertexSplice* const splicep = uap->srcp()->cast<DfgVertexSplice>()) {
                if (!splicep->wholep()) return nullptr;
            }
        }
        return vtxp;
    }

    bool foreachDriver(std::function<bool(DfgVertex&, uint32_t, FileLine*)> f) {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
        }
        return false;
    }
    bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t, FileLine*)> f) const {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
        }
        return false;
    }
    bool foreachDriver(std::function<bool(DfgVertex&, uint32_t)> f) {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo)) return true;
        }
        return false;
    }
    bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t)> f) const {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo)) return true;
        }
        return false;
    }
};

class DfgSpliceArray final : public DfgVertexSplice {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgSpliceArray(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexSplice{dfg, dfgType(), flp, dtype} {
        UASSERT_OBJ(isArray(), flp, "Non-array DfgSpliceArray");
    }
    ASTGEN_MEMBERS_DfgSpliceArray;
};

class DfgSplicePacked final : public DfgVertexSplice {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgSplicePacked(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexSplice{dfg, dfgType(), flp, dtype} {
        UASSERT_OBJ(isPacked(), flp, "Non-packed DfgSplicePacked");
    }
    ASTGEN_MEMBERS_DfgSplicePacked;
};

class DfgLogic final : public DfgVertexVariadic {
    // Generic vertex representing a whole combinational process
    AstAlways* const m_nodep;  // The Ast logic represented by this vertex
    AstScope* const m_scopep;  // The AstScope m_nodep is under, iff scoped
    const std::unique_ptr<CfgGraph> m_cfgp;
    std::vector<DfgVertex*> m_synth;  // Vertices this logic was synthesized into
    bool m_selectedForSynthesis = false;  // Logic selected for synthesis
    bool m_nonSynthesizable = false;  // Logic is not synthesizeable (by DfgSynthesis)
    bool m_reverted = false;  // Logic was synthesized (in part if non-synthesizable) then reverted
    mutable uint8_t m_cachedPure = 0;  // Cached purity of the logic

public:
    DfgLogic(DfgGraph& dfg, AstAlways* nodep, AstScope* scopep, std::unique_ptr<CfgGraph> cfgp)
        : DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), DfgDataType::null()}
        , m_nodep{nodep}
        , m_scopep{scopep}
        , m_cfgp{std::move(cfgp)} {}

    ASTGEN_MEMBERS_DfgLogic;

    std::string srcName(size_t) const override final { return ""; }

    // Can only be driven by DfgVertexVar
    void addInput(DfgVertexVar* varp) { newInput()->relinkSrcp(varp); }

    // Accessors
    AstAlways* nodep() const { return m_nodep; }
    AstScope* scopep() const { return m_scopep; }
    CfgGraph& cfg() { return *m_cfgp; }
    const CfgGraph& cfg() const { return *m_cfgp; }
    std::vector<DfgVertex*>& synth() { return m_synth; }
    const std::vector<DfgVertex*>& synth() const { return m_synth; }
    bool selectedForSynthesis() const { return m_selectedForSynthesis; }
    void setSelectedForSynthesis() { m_selectedForSynthesis = true; }
    bool nonSynthesizable() const { return m_nonSynthesizable; }
    void setNonSynthesizable() { m_nonSynthesizable = true; }
    bool reverted() const { return m_reverted; }
    void setReverted() { m_reverted = true; }
    // Logic has no side-effect, just computes its output variables based on its input variables
    bool isPure() const {
        if (!m_cachedPure) {
            // This is a sledgehamer, but AstNodeStmts don't compute their 'purity' properly,
            // not that 'purity' makes sense for statements... We don't call this often and cached.
            const bool pure = m_nodep->forall([](AstNode* nodep) { return nodep->isPure(); });
            m_cachedPure = static_cast<uint8_t>(pure) | 0x2;
        }
        return m_cachedPure & 0x01;
    }
};

class DfgUnresolved final : public DfgVertexVariadic {
    // Represents a collection of unresolved variable drivers before synthesis
public:
    DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp)
        : DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtype()} {}
    ASTGEN_MEMBERS_DfgUnresolved;

    std::string srcName(size_t) const override final { return ""; }

    // Can only be driven by DfgLogic or DfgVertexSplice
    void addDriver(DfgLogic* vtxp) { newInput()->relinkSrcp(vtxp); }
    void addDriver(DfgVertexSplice* vtxp) { newInput()->relinkSrcp(vtxp); }
};

//------------------------------------------------------------------------------
// The rest of the vertex types are generated by 'astgen' from AstNodeExpr
#include "V3Dfg__gen_auto_classes.h"

//------------------------------------------------------------------------------
// Inline method definitions

#endif
