verilator/src/V3DfgVertices.h

512 lines
20 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: DfgVertex sub-classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 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.
// 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'.
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
AstVarScope* const m_varScopep; // 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 Var/VarScope it stands
// for. It might point to m_varp/m_varScopep itself to indicate it's a temporary without an
// associated input Var/VarScope.
AstNode* m_tmpForp = nullptr;
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, AstVarScope* vscp)
: DfgVertex{dfg, type, varp->fileline(), *DfgDataType::fromAst(varp->dtypep())}
, m_varp{varp}
, m_varScopep{vscp} {
#ifdef VL_DEBUG
if (v3Global.rootp()->topScopep()) {
UASSERT_OBJ(vscp, varp, "Un-scoped DfgVertexVar created in scoped DfgGraph");
} else {
UASSERT_OBJ(!vscp, varp, "Scoped DfgVertexVar created in un-scoped DfgGraph");
}
#endif
// Increment reference count
AstNode* const variablep = nodep();
variablep->user1(variablep->user1() + 0x10);
UASSERT_OBJ((variablep->user1() >> 4) > 0, variablep, "Reference count overflow");
// Allocate sources
newInput();
newInput();
}
protected:
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp)
: DfgVertexVar{dfg, type, varp, nullptr} {}
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
: DfgVertexVar{dfg, type, vscp->varp(), vscp} {}
public:
~DfgVertexVar() {
// Decrement reference count
AstNode* const variablep = nodep();
variablep->user1(variablep->user1() - 0x10);
UASSERT_OBJ((variablep->user1() >> 4) >= 0, variablep, "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* varScopep() const { return m_varScopep; }
AstNode* nodep() const {
return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
}
// 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.
AstNode* tmpForp() const { return m_tmpForp; }
void tmpForp(AstNode* 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 nodep()->user1() >> 5; } // I.e.: (nodep()->user1() >> 4) > 1
// Variable referenced from Ast code in the same module/netlist
static bool hasModRdRefs(const AstNode* nodep) { return nodep->user1() & 0x04; }
static bool hasModWrRefs(const AstNode* nodep) { return nodep->user1() & 0x08; }
static void setHasModRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x04); }
static void setHasModWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x08); }
bool hasModRdRefs() const { return hasModRdRefs(nodep()); }
bool hasModWrRefs() const { return hasModWrRefs(nodep()); }
bool hasModRefs() const { return hasModRdRefs() || hasModWrRefs(); }
void setHasModRdRefs() const { setHasModRdRefs(nodep()); }
void setHasModWrRefs() const { setHasModWrRefs(nodep()); }
// Variable referenced outside the containing module/netlist.
static bool hasExtRdRefs(const AstNode* nodep) { return nodep->user1() & 0x01; }
static bool hasExtWrRefs(const AstNode* nodep) { return nodep->user1() & 0x02; }
static void setHasExtRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x01); }
static void setHasExtWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x02); }
bool hasExtRdRefs() const { return hasExtRdRefs(nodep()); }
bool hasExtWrRefs() const { return hasExtWrRefs(nodep()); }
bool hasExtRefs() const { return hasExtRdRefs() || hasExtWrRefs(); }
// 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 AstNode* nodep) {
return hasModWrRefs(nodep) || hasExtWrRefs(nodep);
}
bool isVolatile() const { return isVolatile(nodep()); }
};
class DfgVarArray final : public DfgVertexVar {
friend class DfgVertex;
friend class DfgVisitor;
public:
DfgVarArray(DfgGraph& dfg, AstVar* varp)
: DfgVertexVar{dfg, dfgType(), varp} {
UASSERT_OBJ(isArray(), varp, "Non-array DfgVarArray");
}
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, AstVar* varp)
: DfgVertexVar{dfg, dfgType(), varp} {
UASSERT_OBJ(isPacked(), varp, "Non-packed DfgVarPacked");
}
DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
: DfgVertexVar{dfg, dfgType(), vscp} {
UASSERT_OBJ(isPacked(), vscp, "Non-packed DfgVarPacked");
}
ASTGEN_MEMBERS_DfgVarPacked;
};
//------------------------------------------------------------------------------
// 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 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
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; }
};
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