DFG: Make implementation more similar to AST

Use the same style, and reuse the bulk of astgen to generate DfgVertex
related code. In particular allow for easier definition of custom
DfgVertex sub-types that do not directly correspond to an AstNode
sub-type. Also introduces specific names for the fixed arity vertices.
No functional change intended.
This commit is contained in:
Geza Lore 2022-10-04 11:03:41 +01:00
parent 56ac054fb2
commit 965d99f1bc
14 changed files with 1263 additions and 1085 deletions

View File

@ -1047,9 +1047,9 @@ Generating ``AstNode`` members
Some of the member s of ``AstNode`` sub-classes are generated by ``astgen``.
These are emitted as pre-processor macro definitions, which then need to be
added to the ``AstNode`` sub-classes they correspond to. Specifically ``class
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_Foo;`` at class scope.
The ``astgen`` script checks and errors if this is not present. The method
generated depends on whether the class is a concrete final class, or an
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_AstFoo;`` at class
scope. The ``astgen`` script checks and errors if this is not present. The
method generated depends on whether the class is a concrete final class, or an
abstract ``AstNode*`` base-class, and on ``@astgen`` directives present in
comment sections in the body of the ``AstNode`` sub-class definitions.

View File

@ -289,6 +289,7 @@ NON_STANDALONE_HEADERS = \
V3AstNodeDType.h \
V3AstNodeMath.h \
V3AstNodeOther.h \
V3DfgVertices.h \
V3WidthCommit.h \
AST_DEFS := \
@ -296,10 +297,19 @@ AST_DEFS := \
V3AstNodeMath.h \
V3AstNodeOther.h \
DFG_DEFS := \
V3DfgVertices.h
#### astgen common flags
ASTGENFLAGS = -I $(srcdir)
ASTGENFLAGS += $(foreach f,$(AST_DEFS),--astdef $f)
ASTGENFLAGS += $(foreach f,$(DFG_DEFS),--dfgdef $f)
#### Linking
ifeq ($(VL_VLCOV),)
PREDEP_H = V3Ast__gen_classes.h
PREDEP_H = V3Ast__gen_forward_class_decls.h
OBJS += $(RAW_OBJS) $(NC_OBJS)
else
PREDEP_H =
@ -318,8 +328,8 @@ V3Number_test: V3Number_test.o
#### Modules
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS)
$(PYTHON3) $(ASTGEN) -I $(srcdir) $(foreach f,$(AST_DEFS),--astdef $f) $*.cpp
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
%.o: %.cpp
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
@ -341,7 +351,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
#### Generated files
# Target rule called before parallel build to make generated files
serial:: V3Ast__gen_classes.h V3ParseBison.c
serial:: V3Ast__gen_forward_class_decls.h V3ParseBison.c
serial_vlcov:: vlcovgen.d
@ -349,8 +359,8 @@ vlcovgen.d: $(VLCOVGEN) $(srcdir)/../include/verilated_cov_key.h
$(PYTHON3) $(VLCOVGEN) --srcdir $(srcdir)
touch $@
V3Ast__gen_classes.h : $(ASTGEN) $(AST_DEFS)
$(PYTHON3) $(ASTGEN) -I $(srcdir) $(foreach f,$(AST_DEFS),--astdef $f) --classes
V3Ast__gen_forward_class_decls.h: $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) --classes
V3ParseBison.h: V3ParseBison.c

View File

@ -26,7 +26,7 @@
#include "V3Global.h"
#include "V3Number.h"
#include "V3Ast__gen_classes.h" // From ./astgen
#include "V3Ast__gen_forward_class_decls.h" // From ./astgen
#include <cmath>
#include <functional>
@ -87,7 +87,7 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
class VNType final {
public:
#include "V3Ast__gen_types.h" // From ./astgen
#include "V3Ast__gen_type_enum.h" // From ./astgen
// Above include has:
// enum en {...};
// const char* ascii() const {...};
@ -2179,7 +2179,7 @@ void AstNode::addPrev(AstNode* newp) {
}
// Specialisations of privateTypeTest
#include "V3Ast__gen_impl.h" // From ./astgen
#include "V3Ast__gen_type_tests.h" // From ./astgen
// Specializations of AstNode::mayBeUnder
template <>
@ -2478,7 +2478,7 @@ AstNode* VNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
return nodep->iterateSubtreeReturnEdits(*this);
}
// Include macros generated by 'astgen'. These include ASTGEN_MEMBERS_<Node>
// Include macros generated by 'astgen'. These include ASTGEN_MEMBERS_Ast<Node>
// for each AstNode sub-type, and ASTGEN_SUPER_<Node> for concrete final
// AstNode sub-types. The generated members include boilerplate methods related
// to cloning, visitor dispatch, and other functionality. ASTGEN_SUPER_<Node>

View File

@ -50,7 +50,7 @@ protected:
: AstNode{t, fl} {}
public:
ASTGEN_MEMBERS_NodeDType;
ASTGEN_MEMBERS_AstNodeDType;
// ACCESSORS
void dump(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const;
@ -144,7 +144,7 @@ protected:
: AstNodeDType{t, fl} {}
public:
ASTGEN_MEMBERS_NodeArrayDType;
ASTGEN_MEMBERS_AstNodeArrayDType;
void dump(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override;
const char* broken() const override {
@ -212,7 +212,7 @@ protected:
}
public:
ASTGEN_MEMBERS_NodeUOrStructDType;
ASTGEN_MEMBERS_AstNodeUOrStructDType;
int uniqueNum() const { return m_uniqueNum; }
const char* broken() const override;
void dump(std::ostream& str) const override;
@ -270,7 +270,7 @@ public:
this->rangep(rangep);
this->valuep(valuep);
}
ASTGEN_MEMBERS_EnumItem;
ASTGEN_MEMBERS_AstEnumItem;
string name() const override { return m_name; }
bool maybePointedTo() const override { return true; }
bool hasDType() const override { return true; }
@ -300,7 +300,7 @@ public:
keyDTypep(keyDtp);
dtypep(dtp);
}
ASTGEN_MEMBERS_AssocArrayDType;
ASTGEN_MEMBERS_AstAssocArrayDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -385,7 +385,7 @@ private:
AstRange* rangep);
public:
ASTGEN_MEMBERS_BasicDType;
ASTGEN_MEMBERS_AstBasicDType;
void dump(std::ostream& str) const override;
// width/widthMin/numeric compared elsewhere
bool same(const AstNode* samep) const override {
@ -467,7 +467,7 @@ public:
this->childDTypep(childDTypep);
this->elementsp(elementsp);
}
ASTGEN_MEMBERS_BracketArrayDType;
ASTGEN_MEMBERS_AstBracketArrayDType;
bool similarDType(AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); }
AstNodeDType* subDTypep() const override { return childDTypep(); }
// METHODS
@ -495,7 +495,7 @@ public:
this->dtypep(this);
this->addParamsp(paramsp);
}
ASTGEN_MEMBERS_ClassRefDType;
ASTGEN_MEMBERS_AstClassRefDType;
// METHODS
const char* broken() const override;
void cloneRelink() override;
@ -539,7 +539,7 @@ public:
dtypep(nullptr); // V3Width will resolve
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_ConstDType;
ASTGEN_MEMBERS_AstConstDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -593,7 +593,7 @@ public:
childDTypep(dtp); // Only for parser
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_DefImplicitDType;
ASTGEN_MEMBERS_AstDefImplicitDType;
int uniqueNum() const { return m_uniqueNum; }
bool same(const AstNode* samep) const override {
const AstDefImplicitDType* const sp = static_cast<const AstDefImplicitDType*>(samep);
@ -635,7 +635,7 @@ public:
refDTypep(dtp);
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_DynArrayDType;
ASTGEN_MEMBERS_AstDynArrayDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -677,7 +677,7 @@ public:
: ASTGEN_SUPER_EmptyQueueDType(fl) {
dtypep(this);
}
ASTGEN_MEMBERS_EmptyQueueDType;
ASTGEN_MEMBERS_AstEmptyQueueDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
@ -716,7 +716,7 @@ public:
dtypep(nullptr); // V3Width will resolve
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_EnumDType;
ASTGEN_MEMBERS_AstEnumDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -777,7 +777,7 @@ public:
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{modport} {}
ASTGEN_MEMBERS_IfaceRefDType;
ASTGEN_MEMBERS_AstIfaceRefDType;
// METHODS
const char* broken() const override;
void dump(std::ostream& str = std::cout) const override;
@ -832,7 +832,7 @@ public:
dtypep(this);
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_MemberDType;
ASTGEN_MEMBERS_AstMemberDType;
string name() const override { return m_name; } // * = Var name
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
@ -889,7 +889,7 @@ public:
childDTypep(dtp); // Only for parser
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_ParamTypeDType;
ASTGEN_MEMBERS_AstParamTypeDType;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); }
AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
@ -923,7 +923,7 @@ class AstParseTypeDType final : public AstNodeDType {
public:
explicit AstParseTypeDType(FileLine* fl)
: ASTGEN_SUPER_ParseTypeDType(fl) {}
ASTGEN_MEMBERS_ParseTypeDType;
ASTGEN_MEMBERS_AstParseTypeDType;
AstNodeDType* dtypep() const { return nullptr; }
// METHODS
bool similarDType(AstNodeDType* samep) const override { return this == samep; }
@ -960,7 +960,7 @@ public:
refDTypep(dtp);
dtypep(dtp);
}
ASTGEN_MEMBERS_QueueDType;
ASTGEN_MEMBERS_AstQueueDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -1026,7 +1026,7 @@ public:
: ASTGEN_SUPER_RefDType(fl) {
this->typeofp(typeofp);
}
ASTGEN_MEMBERS_RefDType;
ASTGEN_MEMBERS_AstRefDType;
// METHODS
const char* broken() const override;
void cloneRelink() override;
@ -1101,7 +1101,7 @@ public:
refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_UnsizedArrayDType;
ASTGEN_MEMBERS_AstUnsizedArrayDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -1134,7 +1134,7 @@ public:
: ASTGEN_SUPER_VoidDType(fl) {
dtypep(this);
}
ASTGEN_MEMBERS_VoidDType;
ASTGEN_MEMBERS_AstVoidDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
@ -1166,7 +1166,7 @@ public:
refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_WildcardArrayDType;
ASTGEN_MEMBERS_AstWildcardArrayDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
@ -1199,7 +1199,7 @@ class AstPackArrayDType final : public AstNodeArrayDType {
public:
inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
ASTGEN_MEMBERS_PackArrayDType;
ASTGEN_MEMBERS_AstPackArrayDType;
string prettyDTypeName() const override;
bool isCompound() const override { return false; }
};
@ -1226,7 +1226,7 @@ public:
// width and signing from the subDType/base type
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_UnpackArrayDType;
ASTGEN_MEMBERS_AstUnpackArrayDType;
string prettyDTypeName() const override;
bool same(const AstNode* samep) const override {
const AstUnpackArrayDType* const sp = static_cast<const AstUnpackArrayDType*>(samep);
@ -1244,7 +1244,7 @@ public:
// VSigning below is mispurposed to indicate if packed or not
AstStructDType(FileLine* fl, VSigning numericUnpack)
: ASTGEN_SUPER_StructDType(fl, numericUnpack) {}
ASTGEN_MEMBERS_StructDType;
ASTGEN_MEMBERS_AstStructDType;
string verilogKwd() const override { return "struct"; }
};
class AstUnionDType final : public AstNodeUOrStructDType {
@ -1253,7 +1253,7 @@ public:
// VSigning below is mispurposed to indicate if packed or not
AstUnionDType(FileLine* fl, VSigning numericUnpack)
: ASTGEN_SUPER_UnionDType(fl, numericUnpack) {}
ASTGEN_MEMBERS_UnionDType;
ASTGEN_MEMBERS_AstUnionDType;
string verilogKwd() const override { return "union"; }
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -208,8 +208,7 @@ class ExtractCyclicComponents final {
// The extracted cyclic components
std::vector<std::unique_ptr<DfgGraph>> m_components;
// Map from 'variable vertex' -> 'component index' -> 'clone in that component'
std::unordered_map<const DfgVertexLValue*, std::unordered_map<size_t, DfgVertexLValue*>>
m_clones;
std::unordered_map<const DfgVertexVar*, std::unordered_map<size_t, DfgVertexVar*>> m_clones;
// METHODS
@ -285,7 +284,7 @@ class ExtractCyclicComponents final {
void visitMergeSCCs(const DfgVertex& vtx, size_t targetComponent) {
// We stop at variable boundaries, which is where we will split the graphs
if (vtx.is<DfgVarPacked>() || vtx.is<DfgVarArray>()) return;
if (vtx.is<DfgVertexVar>()) return;
// Mark visited/move on if already visited
if (!m_merged.insert(&vtx).second) return;
@ -312,9 +311,9 @@ class ExtractCyclicComponents final {
// Methods for extraction
// Retrieve clone of vertex in the given component
DfgVertexLValue& getClone(DfgVertexLValue& vtx, size_t component) {
DfgVertexVar& getClone(DfgVertexVar& vtx, size_t component) {
UASSERT_OBJ(m_state.at(&vtx).component != component, &vtx, "Vertex is in that component");
DfgVertexLValue*& clonep = m_clones[&vtx][component];
DfgVertexVar*& clonep = m_clones[&vtx][component];
if (!clonep) {
DfgGraph& dfg = component == 0 ? m_dfg : *m_components[component - 1];
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
@ -322,7 +321,7 @@ class ExtractCyclicComponents final {
} else if (DfgVarArray* const aVtxp = vtx.cast<DfgVarArray>()) {
clonep = new DfgVarArray{dfg, aVtxp->varp()};
}
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexLValue' sub-type");
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type");
if (VL_UNLIKELY(m_doExpensiveChecks)) {
// Assign component number of clone for later checks
m_state
@ -338,30 +337,30 @@ class ExtractCyclicComponents final {
return *clonep;
}
// Fix up non-variable sources of a DfgVertexLValue that are in a different component,
// Fix up non-variable sources of a DfgVertexVar that are in a different component,
// using the provided 'relink' callback
template <typename T_Vertex>
void fixSources(T_Vertex& vtx, std::function<void(T_Vertex&, DfgVertex&, size_t)> relink) {
static_assert(std::is_base_of<DfgVertexLValue, T_Vertex>::value,
"'Vertex' must be a 'DfgVertexLValue'");
static_assert(std::is_base_of<DfgVertexVar, T_Vertex>::value,
"'Vertex' must be a 'DfgVertexVar'");
const size_t component = m_state.at(&vtx).component;
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
DfgVertex& source = *edge.sourcep();
// DfgVertexLValue sources are fixed up by `fixSinks` on those sources
if (source.is<DfgVarPacked>() || source.is<DfgVarArray>()) return;
// DfgVertexVar sources are fixed up by `fixSinks` on those sources
if (source.is<DfgVertexVar>()) return;
const size_t sourceComponent = m_state.at(&source).component;
// Same component is OK
if (sourceComponent == component) return;
// Unlink the source edge (source is reconnected by 'relink'
edge.unlinkSource();
// Apply the fixup
DfgVertexLValue& clone = getClone(vtx, sourceComponent);
DfgVertexVar& clone = getClone(vtx, sourceComponent);
relink(*(clone.as<T_Vertex>()), source, idx);
});
}
// Fix up sinks of given variable vertex that are in a different component
void fixSinks(DfgVertexLValue& vtx) {
void fixSinks(DfgVertexVar& vtx) {
const size_t component = m_state.at(&vtx).component;
vtx.forEachSinkEdge([&](DfgEdge& edge) {
const size_t sinkComponent = m_state.at(edge.sinkp()).component;
@ -400,7 +399,7 @@ class ExtractCyclicComponents final {
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) {
DfgVertex& source = *edge.sourcep();
// OK to cross at variables
if (source.is<DfgVarPacked>() || source.is<DfgVarArray>()) return;
if (source.is<DfgVertexVar>()) return;
UASSERT_OBJ(component == m_state.at(&source).component, &vtx,
"Component crossing edge without variable involvement");
});
@ -670,9 +669,7 @@ static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx)
vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { //
if (edge.sourcep()) {
string headLabel;
if (vtx.arity() > 1 || vtx.is<DfgVarPacked>() || vtx.is<DfgVarArray>()) {
headLabel = vtx.srcName(idx);
}
if (vtx.arity() > 1 || vtx.is<DfgVertexVar>()) headLabel = vtx.srcName(idx);
dumpDotEdge(os, edge, headLabel);
}
});
@ -843,7 +840,7 @@ void DfgEdge::relinkSource(DfgVertex* newSourcep) {
// DfgVertex
//------------------------------------------------------------------------------
DfgVertex::DfgVertex(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep, DfgType type)
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: m_filelinep{flp}
, m_dtypep{dtypep}
, m_type{type} {
@ -897,7 +894,7 @@ V3Hash DfgVertex::hash(HashCache& cache) const {
result += selfHash();
// Variables are defined by themselves, so there is no need to hash the sources. This
// enables sound hashing of graphs circular only through variables, which we rely on.
if (!is<DfgVarPacked>() && !is<DfgVarArray>()) {
if (!is<DfgVertexVar>()) {
forEachSource([&result, &cache](const DfgVertex& src) { result += src.hash(cache); });
}
}
@ -930,7 +927,6 @@ void DfgVertex::replaceWith(DfgVertex* newSorucep) {
//------------------------------------------------------------------------------
// DfgVarPacked ----------
void DfgVarPacked::accept(DfgVisitor& visitor) { visitor.visit(this); }
bool DfgVarPacked::selfEquals(const DfgVertex& that) const {
if (const DfgVarPacked* otherp = that.cast<DfgVarPacked>()) {
@ -943,7 +939,6 @@ bool DfgVarPacked::selfEquals(const DfgVertex& that) const {
V3Hash DfgVarPacked::selfHash() const { return V3Hasher::uncachedHash(varp()); }
// DfgVarPacked ----------
void DfgVarArray::accept(DfgVisitor& visitor) { visitor.visit(this); }
bool DfgVarArray::selfEquals(const DfgVertex& that) const {
if (const DfgVarArray* otherp = that.cast<DfgVarArray>()) {
@ -956,7 +951,6 @@ bool DfgVarArray::selfEquals(const DfgVertex& that) const {
V3Hash DfgVarArray::selfHash() const { return V3Hasher::uncachedHash(varp()); }
// DfgConst ----------
void DfgConst::accept(DfgVisitor& visitor) { visitor.visit(this); }
bool DfgConst::selfEquals(const DfgVertex& that) const {
if (const DfgConst* otherp = that.cast<DfgConst>()) {
@ -971,12 +965,4 @@ V3Hash DfgConst::selfHash() const { return V3Hasher::uncachedHash(m_constp); }
// DfgVisitor
//------------------------------------------------------------------------------
void DfgVisitor::visit(DfgVarPacked* vtxp) { visit(static_cast<DfgVertex*>(vtxp)); }
void DfgVisitor::visit(DfgVarArray* vtxp) { visit(static_cast<DfgVertex*>(vtxp)); }
void DfgVisitor::visit(DfgConst* vtxp) { visit(static_cast<DfgVertex*>(vtxp)); }
//------------------------------------------------------------------------------
// 'astgen' generated definitions
//------------------------------------------------------------------------------
#include "V3Dfg__gen_definitions.h"
#include "V3Dfg__gen_visitor_defns.h" // From ./astgen

View File

@ -21,7 +21,7 @@
// 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 correspondign AstNode sub-types.
// analogous to the corresponding AstNode sub-types.
//
// See also the internals documentation docs/internals.rst
//
@ -39,6 +39,8 @@
#include "V3Hasher.h"
#include "V3List.h"
#include "V3Dfg__gen_forward_class_decls.h" // From ./astgen
#include <algorithm>
#include <array>
#include <functional>
@ -46,7 +48,10 @@
#include <unordered_map>
#include <vector>
class DfgVertex;
#ifndef VL_NOT_FINAL
#define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE
#endif
class DfgEdge;
class DfgVisitor;
@ -63,6 +68,25 @@ struct std::hash<std::pair<const DfgVertex*, const DfgVertex*>> final {
}
};
//------------------------------------------------------------------------------
// Dataflow vertex type enum
//------------------------------------------------------------------------------
class VDfgType final {
public:
#include "V3Dfg__gen_type_enum.h" // From ./astgen
enum en m_e;
VDfgType() = default;
// cppcheck-suppress noExplicitConstructor
constexpr VDfgType(en _e)
: m_e{_e} {}
constexpr operator en() const { return m_e; }
};
constexpr bool operator==(VDfgType lhs, VDfgType rhs) { return lhs.m_e == rhs.m_e; }
constexpr bool operator==(VDfgType lhs, VDfgType::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VDfgType::en lhs, VDfgType rhs) { return lhs == rhs.m_e; }
inline std::ostream& operator<<(std::ostream& os, const VDfgType& t) { return os << t.ascii(); }
//------------------------------------------------------------------------------
// Dataflow graph
//------------------------------------------------------------------------------
@ -191,9 +215,6 @@ public:
// Dataflow graph vertex
//------------------------------------------------------------------------------
// Reuse the generated type constants
using DfgType = VNType;
// Base data flow graph vertex
class DfgVertex VL_NOT_FINAL {
friend class DfgGraph;
@ -206,10 +227,10 @@ protected:
DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex
FileLine* const m_filelinep; // Source location
AstNodeDType* m_dtypep = nullptr; // Data type of the result of this vertex
const DfgType m_type;
const VDfgType m_type;
// CONSTRUCTOR
DfgVertex(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep, DfgType type);
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep);
public:
virtual ~DfgVertex();
@ -277,7 +298,7 @@ public:
// The data type of the result of the nodes
AstNodeDType* dtypep() const { return m_dtypep; }
// The type of this vertex
DfgType type() const { return m_type; }
VDfgType type() const { return m_type; }
// Width of result
uint32_t width() const {
@ -396,11 +417,18 @@ public:
string warnMore() const { return fileline()->warnMore(); }
string warnOther() const { return fileline()->warnOther(); }
private:
// For internal use only.
// Note: specializations for particular vertex types are provided by 'astgen'
template <typename T>
inline static bool privateTypeTest(const DfgVertex* nodep);
public:
// Subtype test
template <typename T>
bool is() const {
static_assert(std::is_base_of<DfgVertex, T>::value, "'T' must be a subtype of DfgVertex");
return m_type == T::dfgType();
return privateTypeTest<typename std::remove_cv<T>::type>(this);
}
// Ensure subtype, then cast to that type
@ -436,367 +464,8 @@ public:
virtual const string srcName(size_t idx) const = 0;
};
// DfgVertices are, well ... DfgVertices
template <>
constexpr bool DfgVertex::is<DfgVertex>() const {
return true;
}
template <>
constexpr DfgVertex* DfgVertex::as<DfgVertex>() {
return this;
}
template <>
constexpr const DfgVertex* DfgVertex::as<DfgVertex>() const {
return this;
}
template <>
constexpr DfgVertex* DfgVertex::cast<DfgVertex>() {
return this;
}
template <>
constexpr const DfgVertex* DfgVertex::cast<DfgVertex>() const {
return this;
}
template <size_t Arity>
class DfgVertexWithArity VL_NOT_FINAL : public DfgVertex {
static_assert(1 <= Arity && Arity <= 4, "Arity must be between 1 and 4 inclusive");
std::array<DfgEdge, Arity> m_srcs; // Source edges
protected:
DfgVertexWithArity<Arity>(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep, DfgType type)
: DfgVertex{dfg, flp, dtypep, type} {
// Initialize source edges
for (size_t i = 0; i < Arity; ++i) m_srcs[i].init(this);
}
~DfgVertexWithArity<Arity>() override = default;
public:
std::pair<DfgEdge*, size_t> sourceEdges() override { //
return {m_srcs.data(), Arity};
}
std::pair<const DfgEdge*, size_t> sourceEdges() const override {
return {m_srcs.data(), Arity};
}
template <size_t Index>
DfgEdge* sourceEdge() {
static_assert(Index < Arity, "Source index out of range");
return &m_srcs[Index];
}
template <size_t Index>
const DfgEdge* sourceEdge() const {
static_assert(Index < Arity, "Source index out of range");
return &m_srcs[Index];
}
template <size_t Index>
DfgVertex* source() const {
static_assert(Index < Arity, "Source index out of range");
return m_srcs[Index].sourcep();
}
template <size_t Index>
void relinkSource(DfgVertex* newSourcep) {
static_assert(Index < Arity, "Source index out of range");
UASSERT_OBJ(m_srcs[Index].sinkp() == this, this, "Inconsistent");
m_srcs[Index].relinkSource(newSourcep);
}
// Named source getter/setter for unary vertices
template <size_t A = Arity>
typename std::enable_if<A == 1, DfgVertex*>::type srcp() const {
static_assert(A == Arity, "Should not be changed");
return source<0>();
}
template <size_t A = Arity>
typename std::enable_if<A == 1, void>::type srcp(DfgVertex* vtxp) {
static_assert(A == Arity, "Should not be changed");
relinkSource<0>(vtxp);
}
// Named source getter/setter for binary vertices
template <size_t A = Arity>
typename std::enable_if<A == 2, DfgVertex*>::type lhsp() const {
static_assert(A == Arity, "Should not be changed");
return source<0>();
}
template <size_t A = Arity>
typename std::enable_if<A == 2, void>::type lhsp(DfgVertex* vtxp) {
static_assert(A == Arity, "Should not be changed");
relinkSource<0>(vtxp);
}
template <size_t A = Arity>
typename std::enable_if<A == 2, DfgVertex*>::type rhsp() const {
static_assert(A == Arity, "Should not be changed");
return source<1>();
}
template <size_t A = Arity>
typename std::enable_if<A == 2, void>::type rhsp(DfgVertex* vtxp) {
static_assert(A == Arity, "Should not be changed");
relinkSource<1>(vtxp);
}
};
class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
DfgEdge* m_srcsp; // The source edges
uint32_t m_srcCnt = 0; // Number of sources used
uint32_t m_srcCap; // Number of sources allocated
// Allocate a new source edge array
DfgEdge* allocSources(size_t n) {
DfgEdge* const srcsp = new DfgEdge[n];
for (size_t i = 0; i < n; ++i) srcsp[i].init(this);
return srcsp;
}
// Double the capacity of m_srcsp
void growSources() {
m_srcCap *= 2;
DfgEdge* const newsp = allocSources(m_srcCap);
for (size_t i = 0; i < m_srcCnt; ++i) {
DfgEdge* const oldp = m_srcsp + i;
// Skip over unlinked source edge
if (!oldp->sourcep()) continue;
// New edge driven from the same vertex as the old edge
newsp[i].relinkSource(oldp->sourcep());
// Unlink the old edge, it will be deleted
oldp->unlinkSource();
}
// Delete old source edges
delete[] m_srcsp;
// Keep hold of new source edges
m_srcsp = newsp;
}
protected:
DfgVertexVariadic(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep, DfgType type,
uint32_t initialCapacity = 1)
: DfgVertex{dfg, flp, dtypep, type}
, m_srcsp{allocSources(initialCapacity)}
, m_srcCap{initialCapacity} {}
~DfgVertexVariadic() override { delete[] m_srcsp; };
DfgEdge* addSource() {
if (m_srcCnt == m_srcCap) growSources();
return m_srcsp + m_srcCnt++;
}
void resetSources() {
// #ifdef VL_DEBUG TODO: DEBUG ONLY
for (uint32_t i = 0; i < m_srcCnt; ++i) {
UASSERT_OBJ(!m_srcsp[i].sourcep(), m_srcsp[i].sourcep(), "Connected source");
}
// #endif
m_srcCnt = 0;
}
public:
DfgEdge* sourceEdge(size_t idx) const { return &m_srcsp[idx]; }
DfgVertex* source(size_t idx) const { return m_srcsp[idx].sourcep(); }
std::pair<DfgEdge*, size_t> sourceEdges() override { return {m_srcsp, m_srcCnt}; }
std::pair<const DfgEdge*, size_t> sourceEdges() const override { return {m_srcsp, m_srcCnt}; }
};
//------------------------------------------------------------------------------
// Vertex classes
//------------------------------------------------------------------------------
class DfgVertexLValue VL_NOT_FINAL : public DfgVertexVariadic {
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module
bool m_hasExtRefs = false; // This AstVar is referenced from outside the module
public:
DfgVertexLValue(DfgGraph& dfg, DfgType type, AstVar* varp, uint32_t initialCapacity)
: DfgVertexVariadic{dfg, varp->fileline(), dtypeFor(varp), type, initialCapacity}
, m_varp{varp} {}
AstVar* varp() const { return m_varp; }
bool hasModRefs() const { return m_hasModRefs; }
void setHasModRefs() { m_hasModRefs = true; }
bool hasExtRefs() const { return m_hasExtRefs; }
void setHasExtRefs() { m_hasExtRefs = true; }
bool hasRefs() const { return m_hasModRefs || m_hasExtRefs; }
// Variable cannot be removed, even if redundant in the DfgGraph (might be used externally)
bool keep() const {
// Keep if referenced outside this module
if (hasExtRefs()) return true;
// Keep if traced
if (v3Global.opt.trace() && varp()->isTrace()) return true;
// Keep if public
if (varp()->isSigPublic()) return true;
// Otherwise it can be removed
return false;
}
};
class DfgVarPacked final : public DfgVertexLValue {
friend class DfgVertex;
friend class DfgVisitor;
using DriverData = std::pair<FileLine*, uint32_t>;
std::vector<DriverData> m_driverData; // Additional data associate with each driver
void accept(DfgVisitor& visitor) override;
bool selfEquals(const DfgVertex& that) const override;
V3Hash selfHash() const override;
static constexpr DfgType dfgType() { return DfgType::atVar; };
public:
DfgVarPacked(DfgGraph& dfg, AstVar* varp)
: DfgVertexLValue{dfg, dfgType(), varp, 1u} {}
bool isDrivenByDfg() const { return arity() > 0; }
bool isDrivenFullyByDfg() const { return arity() == 1 && source(0)->dtypep() == dtypep(); }
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
m_driverData.emplace_back(flp, lsb);
DfgVertexVariadic::addSource()->relinkSource(vtxp);
}
void resetSources() {
m_driverData.clear();
DfgVertexVariadic::resetSources();
}
// Remove undriven sources
void packSources() {
// Grab and reset the driver data
std::vector<DriverData> driverData{std::move(m_driverData)};
// Grab and unlink the sources
std::vector<DfgVertex*> sources{arity()};
forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
sources[idx] = edge.sourcep();
edge.unlinkSource();
});
DfgVertexVariadic::resetSources();
// Add back the driven sources
for (size_t i = 0; i < sources.size(); ++i) {
if (!sources[i]) continue;
addDriver(driverData[i].first, driverData[i].second, sources[i]);
}
}
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; }
const string srcName(size_t idx) const override {
return isDrivenFullyByDfg() ? "" : cvtToStr(driverLsb(idx));
}
};
class DfgVarArray final : public DfgVertexLValue {
friend class DfgVertex;
friend class DfgVisitor;
using DriverData = std::pair<FileLine*, uint32_t>;
std::vector<DriverData> m_driverData; // Additional data associate with each driver
void accept(DfgVisitor& visitor) override;
bool selfEquals(const DfgVertex& that) const override;
V3Hash selfHash() const override;
static constexpr DfgType dfgType() { return DfgType::atUnpackArrayDType; }; // TODO: gross
public:
DfgVarArray(DfgGraph& dfg, AstVar* varp)
: DfgVertexLValue{dfg, dfgType(), varp, 4u} {
UASSERT_OBJ(VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType), varp, "Non array DfgVarArray");
}
bool isDrivenByDfg() const { return arity() > 0; }
void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) {
m_driverData.emplace_back(flp, index);
DfgVertexVariadic::addSource()->relinkSource(vtxp);
}
void resetSources() {
m_driverData.clear();
DfgVertexVariadic::resetSources();
}
// Remove undriven sources
void packSources() {
// Grab and reset the driver data
std::vector<DriverData> driverData{std::move(m_driverData)};
// Grab and unlink the sources
std::vector<DfgVertex*> sources{arity()};
forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
sources[idx] = edge.sourcep();
edge.unlinkSource();
});
DfgVertexVariadic::resetSources();
// Add back the driven sources
for (size_t i = 0; i < sources.size(); ++i) {
if (!sources[i]) continue;
addDriver(driverData[i].first, driverData[i].second, sources[i]);
}
}
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; }
DfgVertex* driverAt(size_t idx) const {
const DfgEdge* const edgep = findSourceEdge([=](const DfgEdge&, size_t i) { //
return driverIndex(i) == idx;
});
return edgep ? edgep->sourcep() : nullptr;
}
const string srcName(size_t idx) const override { return cvtToStr(driverIndex(idx)); }
};
class DfgConst final : public DfgVertex {
friend class DfgVertex;
friend class DfgVisitor;
AstConst* const m_constp; // The AstConst associated with this vertex (owned by this vertex)
void accept(DfgVisitor& visitor) override;
bool selfEquals(const DfgVertex& that) const override;
V3Hash selfHash() const override;
static constexpr DfgType dfgType() { return DfgType::atConst; };
public:
DfgConst(DfgGraph& dfg, AstConst* constp)
: DfgVertex{dfg, constp->fileline(), dtypeFor(constp), dfgType()}
, m_constp{constp} {}
~DfgConst() override { VL_DO_DANGLING(m_constp->deleteTree(), m_constp); }
AstConst* constp() const { return m_constp; }
V3Number& num() const { return m_constp->num(); }
uint32_t toU32() const { return num().toUInt(); }
int32_t toI32() const { return num().toSInt(); }
bool isZero() const { return num().isEqZero(); }
bool isOnes() const { return num().isEqAllOnes(width()); }
std::pair<DfgEdge*, size_t> sourceEdges() override { return {nullptr, 0}; }
std::pair<const DfgEdge*, size_t> sourceEdges() const override { return {nullptr, 0}; }
const string srcName(size_t) const override { // LCOV_EXCL_START
VL_UNREACHABLE;
return "";
} // LCOV_EXCL_STOP
};
// The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeMath nodes
#include "V3Dfg__gen_vertex_classes.h"
// Specialisations of privateTypeTest
#include "V3Dfg__gen_type_tests.h" // From ./astgen
//------------------------------------------------------------------------------
// Dfg vertex visitor
@ -807,10 +476,8 @@ public:
// Dispatch to most specific 'visit' method on 'vtxp'
void iterate(DfgVertex* vtxp) { vtxp->accept(*this); }
virtual void visit(DfgVarPacked* vtxp);
virtual void visit(DfgVarArray* vtxp);
virtual void visit(DfgConst* vtxp);
#include "V3Dfg__gen_visitor_decls.h"
virtual void visit(DfgVertex* nodep) = 0;
#include "V3Dfg__gen_visitor_decls.h" // From ./astgen
};
//------------------------------------------------------------------------------
@ -937,6 +604,180 @@ Vertex* DfgVertex::findSink() const {
return findSink<Vertex>([](const Vertex&) { return true; });
}
//------------------------------------------------------------------------------
// DfgVertex sub-types follow
//------------------------------------------------------------------------------
// 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"
//------------------------------------------------------------------------------
// Implementation of dataflow graph vertices with a fixed number of sources
//------------------------------------------------------------------------------
template <size_t Arity>
class DfgVertexWithArity VL_NOT_FINAL : public DfgVertex {
static_assert(1 <= Arity && Arity <= 4, "Arity must be between 1 and 4 inclusive");
std::array<DfgEdge, Arity> m_srcs; // Source edges
protected:
DfgVertexWithArity<Arity>(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertex{dfg, type, flp, dtypep} {
// Initialize source edges
for (size_t i = 0; i < Arity; ++i) m_srcs[i].init(this);
}
~DfgVertexWithArity<Arity>() override = default;
public:
std::pair<DfgEdge*, size_t> sourceEdges() final override { //
return {m_srcs.data(), Arity};
}
std::pair<const DfgEdge*, size_t> sourceEdges() const final override {
return {m_srcs.data(), Arity};
}
template <size_t Index>
DfgEdge* sourceEdge() {
static_assert(Index < Arity, "Source index out of range");
return &m_srcs[Index];
}
template <size_t Index>
const DfgEdge* sourceEdge() const {
static_assert(Index < Arity, "Source index out of range");
return &m_srcs[Index];
}
template <size_t Index>
DfgVertex* source() const {
static_assert(Index < Arity, "Source index out of range");
return m_srcs[Index].sourcep();
}
template <size_t Index>
void relinkSource(DfgVertex* newSourcep) {
static_assert(Index < Arity, "Source index out of range");
UASSERT_OBJ(m_srcs[Index].sinkp() == this, this, "Inconsistent");
m_srcs[Index].relinkSource(newSourcep);
}
};
class DfgVertexUnary VL_NOT_FINAL : public DfgVertexWithArity<1> {
protected:
DfgVertexUnary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexWithArity<1>{dfg, type, flp, dtypep} {}
public:
ASTGEN_MEMBERS_DfgVertexUnary;
// Named getter/setter for sources
DfgVertex* srcp() const { return source<0>(); }
void srcp(DfgVertex* vtxp) { relinkSource<0>(vtxp); }
};
class DfgVertexBinary VL_NOT_FINAL : public DfgVertexWithArity<2> {
protected:
DfgVertexBinary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexWithArity<2>{dfg, type, flp, dtypep} {}
public:
ASTGEN_MEMBERS_DfgVertexBinary;
// Named getter/setter for sources
DfgVertex* lhsp() const { return source<0>(); }
void lhsp(DfgVertex* vtxp) { relinkSource<0>(vtxp); }
DfgVertex* rhsp() const { return source<1>(); }
void rhsp(DfgVertex* vtxp) { relinkSource<1>(vtxp); }
};
class DfgVertexTernary VL_NOT_FINAL : public DfgVertexWithArity<3> {
protected:
DfgVertexTernary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexWithArity<3>{dfg, type, flp, dtypep} {}
public:
ASTGEN_MEMBERS_DfgVertexTernary;
};
//------------------------------------------------------------------------------
// Implementation of dataflow graph vertices with a variable number of sources
//------------------------------------------------------------------------------
class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
DfgEdge* m_srcsp; // The source edges
uint32_t m_srcCnt = 0; // Number of sources used
uint32_t m_srcCap; // Number of sources allocated
// Allocate a new source edge array
DfgEdge* allocSources(size_t n) {
DfgEdge* const srcsp = new DfgEdge[n];
for (size_t i = 0; i < n; ++i) srcsp[i].init(this);
return srcsp;
}
// Double the capacity of m_srcsp
void growSources() {
m_srcCap *= 2;
DfgEdge* const newsp = allocSources(m_srcCap);
for (size_t i = 0; i < m_srcCnt; ++i) {
DfgEdge* const oldp = m_srcsp + i;
// Skip over unlinked source edge
if (!oldp->sourcep()) continue;
// New edge driven from the same vertex as the old edge
newsp[i].relinkSource(oldp->sourcep());
// Unlink the old edge, it will be deleted
oldp->unlinkSource();
}
// Delete old source edges
delete[] m_srcsp;
// Keep hold of new source edges
m_srcsp = newsp;
}
protected:
DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep,
uint32_t initialCapacity = 1)
: DfgVertex{dfg, type, flp, dtypep}
, m_srcsp{allocSources(initialCapacity)}
, m_srcCap{initialCapacity} {}
~DfgVertexVariadic() override { delete[] m_srcsp; };
DfgEdge* addSource() {
if (m_srcCnt == m_srcCap) growSources();
return m_srcsp + m_srcCnt++;
}
void resetSources() {
// #ifdef VL_DEBUG TODO: DEBUG ONLY
for (uint32_t i = 0; i < m_srcCnt; ++i) {
UASSERT_OBJ(!m_srcsp[i].sourcep(), m_srcsp[i].sourcep(), "Connected source");
}
// #endif
m_srcCnt = 0;
}
public:
ASTGEN_MEMBERS_DfgVertexVariadic;
DfgEdge* sourceEdge(size_t idx) const { return &m_srcsp[idx]; }
DfgVertex* source(size_t idx) const { return m_srcsp[idx].sourcep(); }
std::pair<DfgEdge*, size_t> sourceEdges() override { return {m_srcsp, m_srcCnt}; }
std::pair<const DfgEdge*, size_t> sourceEdges() const override { return {m_srcsp, m_srcCnt}; }
};
// DfgVertex subclasses
#include "V3DfgVertices.h"
// The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeMath nodes
#include "V3Dfg__gen_auto_classes.h"
bool DfgVertex::isZero() const {
if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero();
return false;

View File

@ -41,8 +41,8 @@ namespace {
// Create a DfgVertex out of a AstNodeMath. For most AstNodeMath subtypes, this can be done
// automatically. For the few special cases, we provide specializations below
template <typename Vertex>
Vertex* makeVertex(const AstForDfg<Vertex>* nodep, DfgGraph& dfg) {
template <typename Vertex, typename Node>
Vertex* makeVertex(const Node* nodep, DfgGraph& dfg) {
return new Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
}
@ -51,22 +51,22 @@ Vertex* makeVertex(const AstForDfg<Vertex>* nodep, DfgGraph& dfg) {
// LCOV_EXCL_START
// AstCCast changes width, but should not exists where DFG optimization is currently invoked
template <>
DfgCCast* makeVertex<DfgCCast>(const AstCCast*, DfgGraph&) {
DfgCCast* makeVertex<DfgCCast, AstCCast>(const AstCCast*, DfgGraph&) {
return nullptr;
}
// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway
template <>
DfgAtoN* makeVertex<DfgAtoN>(const AstAtoN*, DfgGraph&) {
DfgAtoN* makeVertex<DfgAtoN, AstAtoN>(const AstAtoN*, DfgGraph&) {
return nullptr;
}
// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway
template <>
DfgCompareNN* makeVertex<DfgCompareNN>(const AstCompareNN*, DfgGraph&) {
DfgCompareNN* makeVertex<DfgCompareNN, AstCompareNN>(const AstCompareNN*, DfgGraph&) {
return nullptr;
}
// Unhandled in DfgToAst, but also operates on unpacked arrays which we don't optimize anyway
template <>
DfgSliceSel* makeVertex<DfgSliceSel>(const AstSliceSel*, DfgGraph&) {
DfgSliceSel* makeVertex<DfgSliceSel, AstSliceSel>(const AstSliceSel*, DfgGraph&) {
return nullptr;
}
// LCOV_EXCL_STOP
@ -105,11 +105,11 @@ class AstToDfgVisitor final : public VNVisitor {
m_uncommittedVertices.clear();
}
DfgVertexLValue* getNet(AstVar* varp) {
DfgVertexVar* getNet(AstVar* varp) {
if (!varp->user1p()) {
// Note DfgVertexLValue vertices are not added to m_uncommittedVertices, because we
// Note DfgVertexVar vertices are not added to m_uncommittedVertices, because we
// want to hold onto them via AstVar::user1p, and the AstVar might be referenced via
// multiple AstVarRef instances, so we will never revert a DfgVertexLValue once
// multiple AstVarRef instances, so we will never revert a DfgVertexVar once
// created. We will delete unconnected variable vertices at the end.
if (VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, varp};
@ -122,7 +122,7 @@ class AstToDfgVisitor final : public VNVisitor {
varp->user1p(vtxp);
}
}
return varp->user1u().to<DfgVertexLValue*>();
return varp->user1u().to<DfgVertexVar*>();
}
DfgVertex* getVertex(AstNode* nodep) {

View File

@ -42,8 +42,8 @@ namespace {
// Create an AstNodeMath out of a DfgVertex. For most AstNodeMath subtypes, this can be done
// automatically. For the few special cases, we provide specializations below
template <typename Node, typename... Ops>
Node* makeNode(const DfgForAst<Node>* vtxp, Ops... ops) {
template <typename Node, typename Vertex, typename... Ops>
Node* makeNode(const Vertex* vtxp, Ops... ops) {
Node* const nodep = new Node{vtxp->fileline(), ops...};
UASSERT_OBJ(nodep->width() == static_cast<int>(vtxp->width()), vtxp,
"Incorrect width in AstNode created from DfgVertex "
@ -55,31 +55,31 @@ Node* makeNode(const DfgForAst<Node>* vtxp, Ops... ops) {
// Vertices needing special conversion
template <>
AstExtend* makeNode<AstExtend, AstNodeMath*>( //
AstExtend* makeNode<AstExtend, DfgExtend, AstNodeMath*>( //
const DfgExtend* vtxp, AstNodeMath* op1) {
return new AstExtend{vtxp->fileline(), op1, static_cast<int>(vtxp->width())};
}
template <>
AstExtendS* makeNode<AstExtendS, AstNodeMath*>( //
AstExtendS* makeNode<AstExtendS, DfgExtendS, AstNodeMath*>( //
const DfgExtendS* vtxp, AstNodeMath* op1) {
return new AstExtendS{vtxp->fileline(), op1, static_cast<int>(vtxp->width())};
}
template <>
AstShiftL* makeNode<AstShiftL, AstNodeMath*, AstNodeMath*>( //
AstShiftL* makeNode<AstShiftL, DfgShiftL, AstNodeMath*, AstNodeMath*>( //
const DfgShiftL* vtxp, AstNodeMath* op1, AstNodeMath* op2) {
return new AstShiftL{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())};
}
template <>
AstShiftR* makeNode<AstShiftR, AstNodeMath*, AstNodeMath*>( //
AstShiftR* makeNode<AstShiftR, DfgShiftR, AstNodeMath*, AstNodeMath*>( //
const DfgShiftR* vtxp, AstNodeMath* op1, AstNodeMath* op2) {
return new AstShiftR{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())};
}
template <>
AstShiftRS* makeNode<AstShiftRS, AstNodeMath*, AstNodeMath*>( //
AstShiftRS* makeNode<AstShiftRS, DfgShiftRS, AstNodeMath*, AstNodeMath*>( //
const DfgShiftRS* vtxp, AstNodeMath* op1, AstNodeMath* op2) {
return new AstShiftRS{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())};
}
@ -88,20 +88,21 @@ AstShiftRS* makeNode<AstShiftRS, AstNodeMath*, AstNodeMath*>( //
// Currently unhandled nodes - see corresponding AstToDfg functions
// LCOV_EXCL_START
template <>
AstCCast* makeNode<AstCCast, AstNodeMath*>(const DfgCCast* vtxp, AstNodeMath*) {
AstCCast* makeNode<AstCCast, DfgCCast, AstNodeMath*>(const DfgCCast* vtxp, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented");
}
template <>
AstAtoN* makeNode<AstAtoN, AstNodeMath*>(const DfgAtoN* vtxp, AstNodeMath*) {
AstAtoN* makeNode<AstAtoN, DfgAtoN, AstNodeMath*>(const DfgAtoN* vtxp, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented");
}
template <>
AstCompareNN* makeNode<AstCompareNN, AstNodeMath*, AstNodeMath*>(const DfgCompareNN* vtxp,
AstCompareNN*
makeNode<AstCompareNN, DfgCompareNN, AstNodeMath*, AstNodeMath*>(const DfgCompareNN* vtxp,
AstNodeMath*, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented");
}
template <>
AstSliceSel* makeNode<AstSliceSel, AstNodeMath*, AstNodeMath*, AstNodeMath*>(
AstSliceSel* makeNode<AstSliceSel, DfgSliceSel, AstNodeMath*, AstNodeMath*, AstNodeMath*>(
const DfgSliceSel* vtxp, AstNodeMath*, AstNodeMath*, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented");
}
@ -215,8 +216,7 @@ class DfgToAstVisitor final : DfgVisitor {
// Inline vertices that drive only a single node, or are special
if (!vtx.hasMultipleSinks()) return true;
if (vtx.is<DfgConst>()) return true;
if (vtx.is<DfgVarPacked>()) return true;
if (vtx.is<DfgVarArray>()) return true;
if (vtx.is<DfgVertexVar>()) return true;
if (const DfgArraySel* const selp = vtx.cast<DfgArraySel>()) {
return selp->bitp()->is<DfgConst>();
}

View File

@ -154,7 +154,7 @@ void V3DfgPasses::removeVars(DfgGraph& dfg, DfgRemoveVarsContext& ctx) {
void V3DfgPasses::removeUnused(DfgGraph& dfg) {
const auto processVertex = [&](DfgVertex& vtx) {
// Keep variables
if (vtx.is<DfgVarPacked>() || vtx.is<DfgVarArray>()) return false;
if (vtx.is<DfgVertexVar>()) return false;
// Keep if it has sinks
if (vtx.hasSinks()) return false;
// Unlink and delete vertex

View File

@ -115,7 +115,7 @@ class V3DfgPeephole final : public DfgVisitor {
DfgConst* makeZero(FileLine* flp, uint32_t width) { return makeConst(flp, width, 0); }
// Transformations that apply to all commutative binary vertices
void commutativeBinary(DfgVertexWithArity<2>* vtxp) {
void commutativeBinary(DfgVertexBinary* vtxp) {
DfgVertex* const lhsp = vtxp->source<0>();
DfgVertex* const rhsp = vtxp->source<1>();
// Ensure Const is on left-hand side to simplify other patterns
@ -138,9 +138,9 @@ class V3DfgPeephole final : public DfgVisitor {
}
// If both sides are variable references, order the side in some defined way. This allows
// CSE to later merge 'a op b' with 'b op a'.
if (lhsp->is<DfgVarPacked>() && rhsp->is<DfgVarPacked>()) {
AstVar* const lVarp = lhsp->as<DfgVarPacked>()->varp();
AstVar* const rVarp = rhsp->as<DfgVarPacked>()->varp();
if (lhsp->is<DfgVertexVar>() && rhsp->is<DfgVertexVar>()) {
AstVar* const lVarp = lhsp->as<DfgVertexVar>()->varp();
AstVar* const rVarp = rhsp->as<DfgVertexVar>()->varp();
if (lVarp->name() > rVarp->name()) {
APPLYING(SWAP_VAR_IN_COMMUTATIVE_BINARY) {
vtxp->lhsp(rhsp);
@ -152,12 +152,12 @@ class V3DfgPeephole final : public DfgVisitor {
}
// Transformations that apply to all associative binary vertices
void associativeBinary(DfgVertexWithArity<2>* vtxp) {
void associativeBinary(DfgVertexBinary* vtxp) {
DfgVertex* const lhsp = vtxp->lhsp();
// Make associative trees right leaning (for better CSE opportunities)
if (lhsp->type() == vtxp->type() && !lhsp->hasMultipleSinks()) {
DfgVertexWithArity<2>* const lBinp = static_cast<DfgVertexWithArity<2>*>(lhsp);
DfgVertexBinary* const lBinp = lhsp->as<DfgVertexBinary>();
APPLYING(RIGHT_LEANING_ASSOC) {
vtxp->replaceWith(lBinp);
vtxp->lhsp(lBinp->rhsp());
@ -242,7 +242,7 @@ class V3DfgPeephole final : public DfgVisitor {
newRhsp->rhsp(concatp->rhsp());
// The replacement Vertex
DfgVertexWithArity<2>* const replacementp
DfgVertexBinary* const replacementp
= std::is_same<Vertex, DfgEq>::value
? new DfgAnd{m_dfg, concatp->fileline(), m_bitDType}
: nullptr;
@ -334,7 +334,7 @@ class V3DfgPeephole final : public DfgVisitor {
rReducep->srcp(concatp->rhsp());
// Bitwise reduce the results
DfgVertexWithArity<2>* const replacementp = new Bitwise{m_dfg, flp, m_bitDType};
Bitwise* const replacementp = new Bitwise{m_dfg, flp, m_bitDType};
replacementp->lhsp(lReducep);
replacementp->rhsp(rReducep);
vtxp->replaceWith(replacementp);
@ -362,7 +362,7 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
void optimizeShiftRHS(DfgVertexWithArity<2>* vtxp) {
void optimizeShiftRHS(DfgVertexBinary* vtxp) {
if (const DfgConcat* const concatp = vtxp->rhsp()->cast<DfgConcat>()) {
if (concatp->lhsp()->isZero()) { // Drop redundant zero extension
APPLYING(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) { //
@ -1330,12 +1330,12 @@ class V3DfgPeephole final : public DfgVisitor {
// Process one vertex. Return true if graph changed
bool processVertex(DfgVertex& vtx) {
// Keep DfgVertexLValue vertices in this pass. We will remove them later if they become
// Keep DfgVertexVar vertices in this pass. We will remove them later if they become
// redundant. We want to keep the original variables for non-var vertices that drive
// multiple sinks (otherwise we would need to introduce a temporary, but it is better for
// debugging to keep the original variable name, if one is available), so we can't remove
// redundant variables here.
const bool keep = vtx.is<DfgVarPacked>() || vtx.is<DfgVarArray>();
const bool keep = vtx.is<DfgVertexVar>();
// If it has no sinks (unused), we can remove it
if (!keep && !vtx.hasSinks()) {

229
src/V3DfgVertices.h Normal file
View File

@ -0,0 +1,229 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: DfgVertex sub-classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2022 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
// === Abstract base node types (DfgVertex*) ===================================
class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic {
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module
bool m_hasExtRefs = false; // This AstVar is referenced from outside the module
public:
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity)
: DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity}
, m_varp{varp} {}
ASTGEN_MEMBERS_DfgVertexVar;
AstVar* varp() const { return m_varp; }
bool hasModRefs() const { return m_hasModRefs; }
void setHasModRefs() { m_hasModRefs = true; }
bool hasExtRefs() const { return m_hasExtRefs; }
void setHasExtRefs() { m_hasExtRefs = true; }
bool hasRefs() const { return m_hasModRefs || m_hasExtRefs; }
// Variable cannot be removed, even if redundant in the DfgGraph (might be used externally)
bool keep() const {
// Keep if referenced outside this module
if (hasExtRefs()) return true;
// Keep if traced
if (v3Global.opt.trace() && varp()->isTrace()) return true;
// Keep if public
if (varp()->isSigPublic()) return true;
// Otherwise it can be removed
return false;
}
};
// === Concrete node types =====================================================
// === DfgVertex ===
class DfgConst final : public DfgVertex {
friend class DfgVertex;
friend class DfgVisitor;
AstConst* const m_constp; // The AstConst associated with this vertex (owned by this vertex)
bool selfEquals(const DfgVertex& that) const override;
V3Hash selfHash() const override;
public:
DfgConst(DfgGraph& dfg, AstConst* constp)
: DfgVertex{dfg, dfgType(), constp->fileline(), dtypeFor(constp)}
, m_constp{constp} {}
ASTGEN_MEMBERS_DfgConst;
~DfgConst() override { VL_DO_DANGLING(m_constp->deleteTree(), m_constp); }
AstConst* constp() const { return m_constp; }
V3Number& num() const { return m_constp->num(); }
uint32_t toU32() const { return num().toUInt(); }
int32_t toI32() const { return num().toSInt(); }
bool isZero() const { return num().isEqZero(); }
bool isOnes() const { return num().isEqAllOnes(width()); }
std::pair<DfgEdge*, size_t> sourceEdges() override { return {nullptr, 0}; }
std::pair<const DfgEdge*, size_t> sourceEdges() const override { return {nullptr, 0}; }
const string srcName(size_t) const override { // LCOV_EXCL_START
VL_UNREACHABLE;
return "";
} // LCOV_EXCL_STOP
};
// === DfgVertexVar ===
class DfgVarArray final : public DfgVertexVar {
friend class DfgVertex;
friend class DfgVisitor;
using DriverData = std::pair<FileLine*, uint32_t>;
std::vector<DriverData> m_driverData; // Additional data associate with each driver
bool selfEquals(const DfgVertex& that) const override;
V3Hash selfHash() const override;
public:
DfgVarArray(DfgGraph& dfg, AstVar* varp)
: DfgVertexVar{dfg, dfgType(), varp, 4u} {
UASSERT_OBJ(VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType), varp, "Non array DfgVarArray");
}
ASTGEN_MEMBERS_DfgVarArray;
bool isDrivenByDfg() const { return arity() > 0; }
void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) {
m_driverData.emplace_back(flp, index);
DfgVertexVariadic::addSource()->relinkSource(vtxp);
}
void resetSources() {
m_driverData.clear();
DfgVertexVariadic::resetSources();
}
// Remove undriven sources
void packSources() {
// Grab and reset the driver data
std::vector<DriverData> driverData{std::move(m_driverData)};
// Grab and unlink the sources
std::vector<DfgVertex*> sources{arity()};
forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
sources[idx] = edge.sourcep();
edge.unlinkSource();
});
DfgVertexVariadic::resetSources();
// Add back the driven sources
for (size_t i = 0; i < sources.size(); ++i) {
if (!sources[i]) continue;
addDriver(driverData[i].first, driverData[i].second, sources[i]);
}
}
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; }
DfgVertex* driverAt(size_t idx) const {
const DfgEdge* const edgep = findSourceEdge([=](const DfgEdge&, size_t i) { //
return driverIndex(i) == idx;
});
return edgep ? edgep->sourcep() : nullptr;
}
const string srcName(size_t idx) const override { return cvtToStr(driverIndex(idx)); }
};
class DfgVarPacked final : public DfgVertexVar {
friend class DfgVertex;
friend class DfgVisitor;
using DriverData = std::pair<FileLine*, uint32_t>;
std::vector<DriverData> m_driverData; // Additional data associate with each driver
bool selfEquals(const DfgVertex& that) const override;
V3Hash selfHash() const override;
public:
DfgVarPacked(DfgGraph& dfg, AstVar* varp)
: DfgVertexVar{dfg, dfgType(), varp, 1u} {}
ASTGEN_MEMBERS_DfgVarPacked;
bool isDrivenByDfg() const { return arity() > 0; }
bool isDrivenFullyByDfg() const { return arity() == 1 && source(0)->dtypep() == dtypep(); }
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
m_driverData.emplace_back(flp, lsb);
DfgVertexVariadic::addSource()->relinkSource(vtxp);
}
void resetSources() {
m_driverData.clear();
DfgVertexVariadic::resetSources();
}
// Remove undriven sources
void packSources() {
// Grab and reset the driver data
std::vector<DriverData> driverData{std::move(m_driverData)};
// Grab and unlink the sources
std::vector<DfgVertex*> sources{arity()};
forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
sources[idx] = edge.sourcep();
edge.unlinkSource();
});
DfgVertexVariadic::resetSources();
// Add back the driven sources
for (size_t i = 0; i < sources.size(); ++i) {
if (!sources[i]) continue;
addDriver(driverData[i].first, driverData[i].second, sources[i]);
}
}
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; }
const string srcName(size_t idx) const override {
return isDrivenFullyByDfg() ? "" : cvtToStr(driverLsb(idx));
}
};
#endif

View File

@ -8,12 +8,14 @@ import os
import re
import sys
import textwrap
# from pprint import pprint, pformat
# This class is used to represents both AstNode and DfgVertex sub-types
class Node:
def __init__(self, name, superClass, file, lineno):
def __init__(self, name, superClass, file=None, lineno=None):
self._name = name
self._superClass = superClass
self._subClasses = [] # Initially list, but tuple after completion
@ -173,9 +175,11 @@ class Node:
return self in other.allSubClasses
Nodes = {}
SortedNodes = None
DfgVertices = None
AstNodes = {}
AstNodeList = None
DfgVertices = {}
DfgVertexList = None
ClassRefs = {}
Stages = {}
@ -278,7 +282,7 @@ class Cpt:
self.error("Can't parse from function: " + func)
typen = match.group(1)
subnodes = match.group(2)
if Nodes[typen].isRoot:
if AstNodes[typen].isRoot:
self.error("Unknown AstNode typen: " + typen + ": in " + func)
mif = ""
@ -333,7 +337,7 @@ class Cpt:
elif match_skip:
typen = match_skip.group(1)
self.tree_skip_visit[typen] = 1
if typen not in Nodes:
if typen not in AstNodes:
self.error("Unknown node type: " + typen)
else:
@ -463,7 +467,7 @@ class Cpt:
self.print(
" // Bottom class up, as more simple transforms are generally better\n"
)
for node in SortedNodes:
for node in AstNodeList:
out_for_type_sc = []
out_for_type = []
classes = list(node.allSuperClasses)
@ -502,7 +506,7 @@ class Cpt:
"".join(out_for_type_sc))
if out_for_type[0]:
self.print(" iterateAndNextNull(nodep->rhsp());\n")
if node.isSubClassOf(Nodes["NodeTriop"]):
if node.isSubClassOf(AstNodes["NodeTriop"]):
self.print(
" iterateAndNextNull(nodep->thsp());\n")
self.print("".join(out_for_type) + " }\n")
@ -542,7 +546,7 @@ def parseOpType(string):
return None
def read_types(filename):
def read_types(filename, Nodes, prefix):
hasErrors = False
def error(lineno, message):
@ -560,8 +564,9 @@ def read_types(filename):
return
if not hasAstgenMembers:
error(
node.lineno, "'Ast" + node.name +
"' does not contain 'ASTGEN_MEMBERS_" + node.name + ";'")
node.lineno,
"'{p}{n}' does not contain 'ASTGEN_MEMBERS_{p}{n};'".format(
p=prefix, n=node.name))
hasAstgenMembers = False
with open(filename) as fh:
@ -575,12 +580,12 @@ def read_types(filename):
classn = match.group(2)
match = re.search(r':\s*public\s+(\S+)', line)
supern = match.group(1) if match else ""
if re.search(r'Ast', supern):
classn = re.sub(r'^Ast', '', classn)
supern = re.sub(r'^Ast', '', supern)
if re.search(prefix, supern):
classn = re.sub(r'^' + prefix, '', classn)
supern = re.sub(r'^' + prefix, '', supern)
if not supern:
sys.exit("%Error: 'Ast{}' has no super-class".format(
classn))
sys.exit("%Error: '{p}{c}' has no super-class".format(
p=prefix, c=classn))
checkFinishedNode(node)
superClass = Nodes[supern]
node = Node(classn, superClass, filename, lineno)
@ -589,8 +594,13 @@ def read_types(filename):
if not node:
continue
if re.match(r'^\s*ASTGEN_MEMBERS_' + node.name + ';', line):
if re.match(r'^\s*ASTGEN_MEMBERS_' + prefix + node.name + ';',
line):
hasAstgenMembers = True
if prefix != "Ast":
continue
match = re.match(r'^\s*//\s*@astgen\s+(.*)$', line)
if match:
decl = re.sub(r'//.*$', '', match.group(1))
@ -650,6 +660,59 @@ def read_types(filename):
sys.exit("%Error: Stopping due to errors reported above")
def check_types(sortedTypes, prefix, abstractPrefix):
baseClass = prefix + abstractPrefix
# Check all leaf types are not AstNode* and non-leaves are AstNode*
for node in sortedTypes:
if re.match(r'^' + abstractPrefix, node.name):
if node.isLeaf:
sys.exit(
"%Error: Final {b} subclasses must not be named {b}*: {p}{n}"
.format(b=baseClass, p=prefix, n=node.name))
else:
if not node.isLeaf:
sys.exit(
"%Error: Non-final {b} subclasses must be named {b}*: {p}{n}"
.format(b=baseClass, p=prefix, n=node.name))
# Check ordering of node definitions
hasOrderingError = False
files = tuple(
sorted(set(_.file for _ in sortedTypes if _.file is not None)))
for file in files:
nodes = tuple(filter(lambda _, f=file: _.file == f, sortedTypes))
expectOrder = tuple(sorted(nodes, key=lambda _: (_.isLeaf, _.ordIdx)))
actualOrder = tuple(sorted(nodes, key=lambda _: _.lineno))
expect = {
node: pred
for pred, node in zip((None, ) + expectOrder[:-1], expectOrder)
}
actual = {
node: pred
for pred, node in zip((None, ) + actualOrder[:-1], actualOrder)
}
for node in nodes:
if expect[node] != actual[node]:
hasOrderingError = True
pred = expect[node]
print(
"{file}:{lineno}: %Error: Definition of '{p}{n}' is out of order. Shold be {where}."
.format(file=file,
lineno=node.lineno,
p=prefix,
n=node.name,
where=("right after '" + prefix + pred.name +
"'" if pred else "first in file")),
file=sys.stderr)
if hasOrderingError:
sys.exit(
"%Error: Stopping due to out of order definitions listed above")
def read_stages(filename):
with open(filename) as fh:
n = 100
@ -712,7 +775,7 @@ def write_report(filename):
fh.write(" " + classn + "\n")
fh.write("\nClasses:\n")
for node in SortedNodes:
for node in AstNodeList:
fh.write(" class Ast%-17s\n" % node.name)
fh.write(" arity: {}\n".format(node.arity))
fh.write(" parent: ")
@ -741,97 +804,110 @@ def write_report(filename):
fh.write("\n")
def write_classes(filename):
with open_file(filename) as fh:
fh.write("class AstNode;\n")
for node in SortedNodes:
fh.write("class Ast%-17s // " % (node.name + ";"))
################################################################################
# Common code genaration
################################################################################
def write_forward_class_decls(prefix, nodeList):
with open_file("V3{p}__gen_forward_class_decls.h".format(p=prefix)) as fh:
for node in nodeList:
fh.write("class {p}{n:<17} // ".format(p=prefix,
n=node.name + ";"))
for superClass in node.allSuperClasses:
fh.write("Ast%-12s " % superClass.name)
fh.write("{p}{n:<12} ".format(p=prefix, n=superClass.name))
fh.write("\n")
def write_visitor_decls(filename):
with open_file(filename) as fh:
for node in SortedNodes:
def write_visitor_decls(prefix, nodeList):
with open_file("V3{p}__gen_visitor_decls.h".format(p=prefix)) as fh:
for node in nodeList:
if not node.isRoot:
fh.write("virtual void visit(Ast" + node.name + "*);\n")
fh.write("virtual void visit({p}{n}*);\n".format(p=prefix,
n=node.name))
def write_visitor_defns(filename):
with open_file(filename) as fh:
for node in SortedNodes:
def write_visitor_defns(prefix, nodeList, visitor):
with open_file("V3{p}__gen_visitor_defns.h".format(p=prefix)) as fh:
variable = "nodep" if prefix == "Ast" else "vtxp"
for node in nodeList:
base = node.superClass
if base is not None:
fh.write("void VNVisitor::visit(Ast" + node.name +
"* nodep) { visit(static_cast<Ast" + base.name +
"*>(nodep)); }\n")
def write_impl(filename):
with open_file(filename) as fh:
fh.write("\n")
fh.write("// For internal use. They assume argument is not nullptr.\n")
for node in SortedNodes:
fh.write("template<> inline bool AstNode::privateTypeTest<Ast" +
node.name + ">(const AstNode* nodep) { ")
if node.isRoot:
fh.write("return true; ")
else:
fh.write("return ")
if not node.isLeaf:
fh.write(
"static_cast<int>(nodep->type()) >= static_cast<int>(VNType::first"
+ node.name + ") && ")
fh.write(
"static_cast<int>(nodep->type()) <= static_cast<int>(VNType::last"
+ node.name + "); ")
else:
fh.write("nodep->type() == VNType::at" + node.name + "; ")
fh.write("}\n")
"void {c}::visit({p}{n}* {v}) {{ visit(static_cast<{p}{b}*>({v})); }}\n"
.format(c=visitor,
p=prefix,
n=node.name,
b=base.name,
v=variable))
def write_types(filename):
with open_file(filename) as fh:
def write_type_enum(prefix, nodeList):
root = next(_ for _ in nodeList if _.isRoot)
with open_file("V3{p}__gen_type_enum.h".format(p=prefix)) as fh:
fh.write(" enum en : uint16_t {\n")
for node in sorted(filter(lambda _: _.isLeaf, SortedNodes),
for node in sorted(filter(lambda _: _.isLeaf, nodeList),
key=lambda _: _.typeId):
fh.write(" at" + node.name + " = " + str(node.typeId) +
",\n")
fh.write(" _ENUM_END = " + str(Nodes["Node"].typeIdMax + 1) +
"\n")
fh.write(" at{t} = {n},\n".format(t=node.name,
n=node.typeId))
fh.write(" _ENUM_END = {n}\n".format(n=root.typeIdMax + 1))
fh.write(" };\n")
fh.write(" enum bounds : uint16_t {\n")
for node in sorted(filter(lambda _: not _.isLeaf, SortedNodes),
for node in sorted(filter(lambda _: not _.isLeaf, nodeList),
key=lambda _: _.typeIdMin):
fh.write(" first" + node.name + " = " +
str(node.typeIdMin) + ",\n")
fh.write(" last" + node.name + " = " + str(node.typeIdMax) +
",\n")
fh.write(" first{t} = {n},\n".format(t=node.name,
n=node.typeIdMin))
fh.write(" last{t} = {n},\n".format(t=node.name,
n=node.typeIdMax))
fh.write(" _BOUNDS_END\n")
fh.write(" };\n")
fh.write(" const char* ascii() const {\n")
fh.write(" static const char* const names[_ENUM_END + 1] = {\n")
for node in sorted(filter(lambda _: _.isLeaf, SortedNodes),
for node in sorted(filter(lambda _: _.isLeaf, nodeList),
key=lambda _: _.typeId):
fh.write(" \"" + node.name.upper() + "\",\n")
fh.write(' "{T}",\n'.format(T=node.name.upper()))
fh.write(" \"_ENUM_END\"\n")
fh.write(" };\n")
fh.write(" return names[m_e];\n")
fh.write(" }\n")
def write_yystype(filename):
with open_file(filename) as fh:
for node in SortedNodes:
fh.write("Ast{t}* {m}p;\n".format(t=node.name,
m=node.name[0].lower() +
node.name[1:]))
def write_type_tests(prefix, nodeList):
with open_file("V3{p}__gen_type_tests.h".format(p=prefix)) as fh:
fh.write("// For internal use. They assume argument is not nullptr.\n")
if prefix == "Ast":
base = "AstNode"
variable = "nodep"
enum = "VNType"
elif prefix == "Dfg":
base = "DfgVertex"
variable = "vtxp"
enum = "VDfgType"
for node in nodeList:
fh.write(
"template<> inline bool {b}::privateTypeTest<{p}{n}>(const {b}* {v}) {{ "
.format(b=base, p=prefix, n=node.name, v=variable))
if node.isRoot:
fh.write("return true;")
elif not node.isLeaf:
fh.write(
"return static_cast<int>({v}->type()) >= static_cast<int>({e}::first{t}) && static_cast<int>({v}->type()) <= static_cast<int>({e}::last{t});"
.format(v=variable, e=enum, t=node.name))
else:
fh.write("return {v}->type() == {e}::at{t};".format(
v=variable, e=enum, t=node.name))
fh.write(" }\n")
def write_macros(filename):
################################################################################
# Ast code genaration
################################################################################
def write_ast_macros(filename):
with open_file(filename) as fh:
def emitBlock(pattern, **fmt):
@ -839,8 +915,8 @@ def write_macros(filename):
textwrap.indent(textwrap.dedent(pattern),
" ").format(**fmt).replace("\n", " \\\n"))
for node in SortedNodes:
fh.write("#define ASTGEN_MEMBERS_{t} \\\n".format(t=node.name))
for node in AstNodeList:
fh.write("#define ASTGEN_MEMBERS_Ast{t} \\\n".format(t=node.name))
emitBlock('''\
static Ast{t}* cloneTreeNull(Ast{t}* nodep, bool cloneNextLink) {{
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
@ -860,7 +936,7 @@ def write_macros(filename):
''',
t=node.name)
for n in (1, 2, 3, 4):
for n in range(1, 5):
op = node.getOp(n)
if not op:
continue
@ -907,7 +983,15 @@ def write_macros(filename):
fh.write("\n")
def write_op_checks(filename):
def write_ast_yystype(filename):
with open_file(filename) as fh:
for node in AstNodeList:
fh.write("Ast{t}* {m}p;\n".format(t=node.name,
m=node.name[0].lower() +
node.name[1:]))
def write_ast_op_checks(filename):
with open_file(filename) as fh:
indent = ""
@ -917,7 +1001,7 @@ def write_op_checks(filename):
textwrap.indent(textwrap.dedent(pattern),
indent).format(**fmt))
for node in SortedNodes:
for node in AstNodeList:
if not node.isLeaf:
continue
@ -991,102 +1075,98 @@ def write_op_checks(filename):
''')
def write_dfg_vertex_classes(filename):
################################################################################
# DFG code genaration
################################################################################
def write_dfg_macros(filename):
with open_file(filename) as fh:
fh.write("\n")
for node in DfgVertices:
fh.write("class Dfg{} final : public DfgVertexWithArity<{}> {{\n".
format(node.name, node.arity))
fh.write(" friend class DfgVertex;\n")
fh.write(" friend class DfgVisitor;\n")
fh.write(" void accept(DfgVisitor& visitor) override;\n")
def emitBlock(pattern, **fmt):
fh.write(
" static constexpr DfgType dfgType() {{ return DfgType::at{t}; }};\n"
.format(t=node.name))
fh.write("public:\n")
fh.write(
" Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) : DfgVertexWithArity<{a}>{{dfg, flp, dtypep, dfgType()}} {{}}\n"
.format(t=node.name, a=node.arity))
# Accessors
textwrap.indent(textwrap.dedent(pattern),
" ").format(**fmt).replace("\n", " \\\n"))
for node in DfgVertexList:
fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
if node.isLeaf:
emitBlock('''\
static constexpr VDfgType dfgType() {{ return VDfgType::at{t}; }};
void accept(DfgVisitor& v) override {{ v.visit(this); }}
''',
t=node.name)
for n in range(1, node.arity + 1):
name, _, _ = node.getOp(n)
emitBlock('''\
DfgVertex* {name}() const {{ return source<{n}>(); }}
void {name}(DfgVertex* vtxp) {{ relinkSource<{n}>(vtxp); }}
''',
name=name,
n=n - 1)
operandNames = tuple(
node.getOp(n)[0] for n in range(1, node.arity + 1))
assert not operandNames or len(operandNames) == node.arity
for i, n in enumerate(operandNames):
fh.write(
" DfgVertex* {n}() const {{ return source<{i}>(); }}\n".
format(n=n, i=i))
for i, n in enumerate(operandNames):
fh.write(
" void {n}(DfgVertex* vtxp) {{ relinkSource<{i}>(vtxp); }}\n"
.format(n=n, i=i))
if operandNames:
names = ", ".join(map(lambda _: '"' + _ + '"', operandNames))
emitBlock('''\
const std::string srcName(size_t idx) const override {{
static const char* names[{a}] = {{ {ns} }};
return names[idx];
}}
''',
a=node.arity,
ns=", ".join(
map(lambda _: '"' + _ + '"', operandNames)))
fh.write(
" const string srcName(size_t idx) const override {\n")
fh.write(
" static const char* names[{a}] = {{ {ns} }};\n".
format(a=node.arity, ns=names))
fh.write(" return names[idx];\n")
fh.write(" }\n")
fh.write("};\n")
fh.write("\n")
fh.write("\n")
fh.write("\n\ntemplate<typename Node>\n")
fh.write("struct DfgForAstImpl;\n\n")
for node in DfgVertices:
fh.write("template <>\n")
fh.write(
"struct DfgForAstImpl<Ast{name}> {{\n".format(name=node.name))
fh.write(" using type = Dfg{name};\n".format(name=node.name))
fh.write("};\n")
fh.write("\ntemplate<typename Node>\n")
fh.write("using DfgForAst = typename DfgForAstImpl<Node>::type;\n")
fh.write("\n\ntemplate<typename Vertex>\n")
fh.write("struct AstForDfgImpl;\n\n")
for node in DfgVertices:
fh.write("template <>\n")
fh.write(
"struct AstForDfgImpl<Dfg{name}> {{\n".format(name=node.name))
fh.write(" using type = Ast{name};\n".format(name=node.name))
fh.write("};\n")
fh.write("\ntemplate<typename Vertex>\n")
fh.write("using AstForDfg = typename AstForDfgImpl<Vertex>::type;\n")
" static_assert(true, \"\")\n") # Swallowing the semicolon
def write_dfg_visitor_decls(filename):
def write_dfg_auto_classes(filename):
with open_file(filename) as fh:
fh.write("\n")
fh.write("virtual void visit(DfgVertex*) = 0;\n")
for node in DfgVertices:
fh.write("virtual void visit(Dfg{}*);\n".format(node.name))
def emitBlock(pattern, **fmt):
fh.write(textwrap.dedent(pattern).format(**fmt))
def write_dfg_definitions(filename):
with open_file(filename) as fh:
for node in DfgVertexList:
# Only generate code for automatically derieved leaf nodes
if (node.file is not None) or not node.isLeaf:
continue
emitBlock('''\
class Dfg{t} final : public Dfg{s} {{
public:
Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: Dfg{s}{{dfg, dfgType(), flp, dtypep}} {{}}
ASTGEN_MEMBERS_Dfg{t};
}};
''',
t=node.name,
s=node.superClass.name)
fh.write("\n")
for node in DfgVertices:
fh.write(
"void Dfg{}::accept(DfgVisitor& visitor) {{ visitor.visit(this); }}\n"
.format(node.name))
fh.write("\n")
for node in DfgVertices:
fh.write(
"void DfgVisitor::visit(Dfg{}* vtxp) {{ visit(static_cast<DfgVertex*>(vtxp)); }}\n"
.format(node.name))
def write_dfg_ast_to_dfg(filename):
with open_file(filename) as fh:
fh.write("\n")
for node in DfgVertices:
for node in DfgVertexList:
# Only generate code for automatically derieved leaf nodes
if (node.file is not None) or (not node.isLeaf):
continue
fh.write(
"void visit(Ast{t}* nodep) override {{\n".format(t=node.name))
fh.write(
' UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");\n'
' UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");\n\n'
)
fh.write(" if (unhandled(nodep)) return;\n")
fh.write(" if (unhandled(nodep)) return;\n\n")
for i in range(node.arity):
fh.write(" iterate(nodep->op{j}p());\n".format(j=i + 1))
fh.write(" if (m_foundUnhandled) return;\n")
fh.write(
' UASSERT_OBJ(nodep->op{j}p()->user1p(), nodep, "Child {j} missing Dfg vertex");\n'
.format(j=i + 1))
fh.write("\n")
fh.write(
" Dfg{t}* const vtxp = makeVertex<Dfg{t}>(nodep, *m_dfgp);\n"
.format(t=node.name))
@ -1095,24 +1175,23 @@ def write_dfg_ast_to_dfg(filename):
fh.write(" ++m_ctx.m_nonRepNode;\n")
fh.write(" return;\n")
fh.write(" }\n\n")
fh.write(" m_uncommittedVertices.push_back(vtxp);\n")
for i in range(node.arity):
fh.write(" iterate(nodep->op{j}p());\n".format(j=i + 1))
fh.write(" if (m_foundUnhandled) return;\n")
fh.write(
' UASSERT_OBJ(nodep->op{j}p()->user1p(), nodep, "Child {j} missing Dfg vertex");\n'
.format(j=i + 1))
fh.write(
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to<DfgVertex*>());\n\n"
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to<DfgVertex*>());\n"
.format(i=i, j=i + 1))
fh.write("\n")
fh.write(" m_uncommittedVertices.push_back(vtxp);\n")
fh.write(" nodep->user1p(vtxp);\n")
fh.write("}\n")
def write_dfg_dfg_to_ast(filename):
with open_file(filename) as fh:
fh.write("\n")
for node in DfgVertices:
for node in DfgVertexList:
# Only generate code for automatically derieved leaf nodes
if (node.file is not None) or (not node.isLeaf):
continue
fh.write(
"void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
for i in range(node.arity):
@ -1146,6 +1225,9 @@ parser.add_argument('-I', action='store', help='source code include directory')
parser.add_argument('--astdef',
action='append',
help='add AST definition file (relative to -I)')
parser.add_argument('--dfgdef',
action='append',
help='add DFG definition file (relative to -I)')
parser.add_argument('--classes',
action='store_true',
help='makes class declaration files')
@ -1155,66 +1237,86 @@ parser.add_argument('infiles', nargs='*', help='list of input .cpp filenames')
Args = parser.parse_args()
###############################################################################
# Read AstNode definitions
###############################################################################
# Set up the root AstNode type. It is standalone so we don't need to parse the
# sources for this.
Nodes["Node"] = Node("Node", None, "AstNode", 1)
AstNodes["Node"] = Node("Node", None)
# Read Ast node definitions
# Read AstNode definitions
for filename in Args.astdef:
read_types(os.path.join(Args.I, filename))
read_types(os.path.join(Args.I, filename), AstNodes, "Ast")
# Compute derived properties over the whole AstNode hierarchy
Nodes["Node"].complete()
AstNodes["Node"].complete()
SortedNodes = tuple(map(lambda _: Nodes[_], sorted(Nodes.keys())))
AstNodeList = tuple(map(lambda _: AstNodes[_], sorted(AstNodes.keys())))
for node in SortedNodes:
# Check all leaves are not AstNode* and non-leaves are AstNode*
if re.match(r'^Node', node.name):
if node.isLeaf:
sys.exit(
"%Error: Final AstNode subclasses must not be named AstNode*: Ast"
+ node.name)
else:
check_types(AstNodeList, "Ast", "Node")
###############################################################################
# Read and generate DfgVertex definitions
###############################################################################
# Set up the root DfgVertex type and some other hand-written base types.
# These are standalone so we don't need to parse the sources for this.
DfgVertices["Vertex"] = Node("Vertex", None)
DfgVertices["VertexUnary"] = Node("VertexUnary", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexUnary"])
DfgVertices["VertexBinary"] = Node("VertexBinary", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexBinary"])
DfgVertices["VertexTernary"] = Node("VertexTernary", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexTernary"])
DfgVertices["VertexVariadic"] = Node("VertexVariadic", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexVariadic"])
# Read DfgVertex definitions
for filename in Args.dfgdef:
read_types(os.path.join(Args.I, filename), DfgVertices, "Dfg")
# Add the DfgVertex sub-types automatically derived from AstNode sub-types
for node in AstNodeList:
# Ignore the hierarchy for now
if not node.isLeaf:
sys.exit(
"%Error: Non-final AstNode subclasses must be named AstNode*: Ast"
+ node.name)
continue
DfgBases = (Nodes["NodeUniop"], Nodes["NodeBiop"], Nodes["NodeTriop"])
DfgVertices = tuple(
node for node in SortedNodes
if node.isLeaf and any(node.isSubClassOf(base) for base in DfgBases))
# Ignore any explicitly defined vertex
if node.name in DfgVertices:
continue
# Check ordering of node definitions
files = tuple(sorted(set(_.file for _ in SortedNodes)))
if node.isSubClassOf(AstNodes["NodeUniop"]):
base = DfgVertices["VertexUnary"]
elif node.isSubClassOf(AstNodes["NodeBiop"]):
base = DfgVertices["VertexBinary"]
elif node.isSubClassOf(AstNodes["NodeTriop"]):
base = DfgVertices["VertexTernary"]
else:
continue
hasOrderingError = False
for file in files:
nodes = tuple(filter(lambda _, f=file: _.file == f, SortedNodes))
expectOrder = tuple(sorted(nodes, key=lambda _: (_.isLeaf, _.ordIdx)))
actualOrder = tuple(sorted(nodes, key=lambda _: _.lineno))
expect = {
node: pred
for pred, node in zip((None, ) + expectOrder[:-1], expectOrder)
}
actual = {
node: pred
for pred, node in zip((None, ) + actualOrder[:-1], actualOrder)
}
for node in nodes:
if expect[node] != actual[node]:
hasOrderingError = True
pred = expect[node]
print(file + ":" + str(node.lineno) +
": %Error: Definition of 'Ast" + node.name +
"' is out of order. Should be " +
("right after 'Ast" + pred.name +
"'" if pred else "first in file") + ".",
file=sys.stderr)
vertex = Node(node.name, base)
DfgVertices[node.name] = vertex
base.addSubClass(vertex)
if hasOrderingError:
sys.exit("%Error: Stopping due to out of order definitions listed above")
for n in range(1, node.arity + 1):
op = node.getOp(n)
if op is not None:
name, monad, kind = op
assert monad == "", "Cannot represnt AstNode as DfgVertex"
vertex.addOp(n, name, "", "")
# Compute derived properties over the whole DfgVertex hierarchy
DfgVertices["Vertex"].complete()
DfgVertexList = tuple(map(lambda _: DfgVertices[_],
sorted(DfgVertices.keys())))
check_types(DfgVertexList, "Dfg", "Vertex")
###############################################################################
# Read additional files
###############################################################################
read_stages(Args.I + "/Verilator.cpp")
@ -1224,19 +1326,29 @@ source_files.extend(glob.glob(Args.I + "/*.cpp"))
for filename in source_files:
read_refs(filename)
###############################################################################
# Generate output
###############################################################################
if Args.classes:
write_report("V3Ast__gen_report.txt")
write_classes("V3Ast__gen_classes.h")
write_visitor_decls("V3Ast__gen_visitor_decls.h")
write_visitor_defns("V3Ast__gen_visitor_defns.h")
write_impl("V3Ast__gen_impl.h")
write_types("V3Ast__gen_types.h")
write_yystype("V3Ast__gen_yystype.h")
write_macros("V3Ast__gen_macros.h")
write_op_checks("V3Ast__gen_op_checks.h")
write_dfg_vertex_classes("V3Dfg__gen_vertex_classes.h")
write_dfg_visitor_decls("V3Dfg__gen_visitor_decls.h")
write_dfg_definitions("V3Dfg__gen_definitions.h")
# Write Ast code
write_forward_class_decls("Ast", AstNodeList)
write_visitor_decls("Ast", AstNodeList)
write_visitor_defns("Ast", AstNodeList, "VNVisitor")
write_type_enum("Ast", AstNodeList)
write_type_tests("Ast", AstNodeList)
write_ast_macros("V3Ast__gen_macros.h")
write_ast_yystype("V3Ast__gen_yystype.h")
write_ast_op_checks("V3Ast__gen_op_checks.h")
# Write Dfg code
write_forward_class_decls("Dfg", DfgVertexList)
write_visitor_decls("Dfg", DfgVertexList)
write_visitor_defns("Dfg", DfgVertexList, "DfgVisitor")
write_type_enum("Dfg", DfgVertexList)
write_type_tests("Dfg", DfgVertexList)
write_dfg_macros("V3Dfg__gen_macros.h")
write_dfg_auto_classes("V3Dfg__gen_auto_classes.h")
write_dfg_ast_to_dfg("V3Dfg__gen_ast_to_dfg.h")
write_dfg_dfg_to_ast("V3Dfg__gen_dfg_to_ast.h")