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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ protected:
: AstNode{t, fl} {} : AstNode{t, fl} {}
public: public:
ASTGEN_MEMBERS_NodeDType; ASTGEN_MEMBERS_AstNodeDType;
// ACCESSORS // ACCESSORS
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const; virtual void dumpSmall(std::ostream& str) const;
@ -144,7 +144,7 @@ protected:
: AstNodeDType{t, fl} {} : AstNodeDType{t, fl} {}
public: public:
ASTGEN_MEMBERS_NodeArrayDType; ASTGEN_MEMBERS_AstNodeArrayDType;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
const char* broken() const override { const char* broken() const override {
@ -212,7 +212,7 @@ protected:
} }
public: public:
ASTGEN_MEMBERS_NodeUOrStructDType; ASTGEN_MEMBERS_AstNodeUOrStructDType;
int uniqueNum() const { return m_uniqueNum; } int uniqueNum() const { return m_uniqueNum; }
const char* broken() const override; const char* broken() const override;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
@ -270,7 +270,7 @@ public:
this->rangep(rangep); this->rangep(rangep);
this->valuep(valuep); this->valuep(valuep);
} }
ASTGEN_MEMBERS_EnumItem; ASTGEN_MEMBERS_AstEnumItem;
string name() const override { return m_name; } string name() const override { return m_name; }
bool maybePointedTo() const override { return true; } bool maybePointedTo() const override { return true; }
bool hasDType() const override { return true; } bool hasDType() const override { return true; }
@ -300,7 +300,7 @@ public:
keyDTypep(keyDtp); keyDTypep(keyDtp);
dtypep(dtp); dtypep(dtp);
} }
ASTGEN_MEMBERS_AssocArrayDType; ASTGEN_MEMBERS_AstAssocArrayDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -385,7 +385,7 @@ private:
AstRange* rangep); AstRange* rangep);
public: public:
ASTGEN_MEMBERS_BasicDType; ASTGEN_MEMBERS_AstBasicDType;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
// width/widthMin/numeric compared elsewhere // width/widthMin/numeric compared elsewhere
bool same(const AstNode* samep) const override { bool same(const AstNode* samep) const override {
@ -467,7 +467,7 @@ public:
this->childDTypep(childDTypep); this->childDTypep(childDTypep);
this->elementsp(elementsp); this->elementsp(elementsp);
} }
ASTGEN_MEMBERS_BracketArrayDType; ASTGEN_MEMBERS_AstBracketArrayDType;
bool similarDType(AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); } bool similarDType(AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); }
AstNodeDType* subDTypep() const override { return childDTypep(); } AstNodeDType* subDTypep() const override { return childDTypep(); }
// METHODS // METHODS
@ -495,7 +495,7 @@ public:
this->dtypep(this); this->dtypep(this);
this->addParamsp(paramsp); this->addParamsp(paramsp);
} }
ASTGEN_MEMBERS_ClassRefDType; ASTGEN_MEMBERS_AstClassRefDType;
// METHODS // METHODS
const char* broken() const override; const char* broken() const override;
void cloneRelink() override; void cloneRelink() override;
@ -539,7 +539,7 @@ public:
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
widthFromSub(subDTypep()); widthFromSub(subDTypep());
} }
ASTGEN_MEMBERS_ConstDType; ASTGEN_MEMBERS_AstConstDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -593,7 +593,7 @@ public:
childDTypep(dtp); // Only for parser childDTypep(dtp); // Only for parser
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
} }
ASTGEN_MEMBERS_DefImplicitDType; ASTGEN_MEMBERS_AstDefImplicitDType;
int uniqueNum() const { return m_uniqueNum; } int uniqueNum() const { return m_uniqueNum; }
bool same(const AstNode* samep) const override { bool same(const AstNode* samep) const override {
const AstDefImplicitDType* const sp = static_cast<const AstDefImplicitDType*>(samep); const AstDefImplicitDType* const sp = static_cast<const AstDefImplicitDType*>(samep);
@ -635,7 +635,7 @@ public:
refDTypep(dtp); refDTypep(dtp);
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
} }
ASTGEN_MEMBERS_DynArrayDType; ASTGEN_MEMBERS_AstDynArrayDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -677,7 +677,7 @@ public:
: ASTGEN_SUPER_EmptyQueueDType(fl) { : ASTGEN_SUPER_EmptyQueueDType(fl) {
dtypep(this); dtypep(this);
} }
ASTGEN_MEMBERS_EmptyQueueDType; ASTGEN_MEMBERS_AstEmptyQueueDType;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; } bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; } bool maybePointedTo() const override { return true; }
@ -716,7 +716,7 @@ public:
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
widthFromSub(subDTypep()); widthFromSub(subDTypep());
} }
ASTGEN_MEMBERS_EnumDType; ASTGEN_MEMBERS_AstEnumDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -777,7 +777,7 @@ public:
, m_cellName{cellName} , m_cellName{cellName}
, m_ifaceName{ifaceName} , m_ifaceName{ifaceName}
, m_modportName{modport} {} , m_modportName{modport} {}
ASTGEN_MEMBERS_IfaceRefDType; ASTGEN_MEMBERS_AstIfaceRefDType;
// METHODS // METHODS
const char* broken() const override; const char* broken() const override;
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
@ -832,7 +832,7 @@ public:
dtypep(this); dtypep(this);
widthFromSub(subDTypep()); widthFromSub(subDTypep());
} }
ASTGEN_MEMBERS_MemberDType; ASTGEN_MEMBERS_AstMemberDType;
string name() const override { return m_name; } // * = Var name string name() const override { return m_name; } // * = Var name
bool hasDType() const override { return true; } bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; } bool maybePointedTo() const override { return true; }
@ -889,7 +889,7 @@ public:
childDTypep(dtp); // Only for parser childDTypep(dtp); // Only for parser
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
} }
ASTGEN_MEMBERS_ParamTypeDType; ASTGEN_MEMBERS_AstParamTypeDType;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); } AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); }
AstBasicDType* basicp() const override { return subDTypep()->basicp(); } AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
@ -923,7 +923,7 @@ class AstParseTypeDType final : public AstNodeDType {
public: public:
explicit AstParseTypeDType(FileLine* fl) explicit AstParseTypeDType(FileLine* fl)
: ASTGEN_SUPER_ParseTypeDType(fl) {} : ASTGEN_SUPER_ParseTypeDType(fl) {}
ASTGEN_MEMBERS_ParseTypeDType; ASTGEN_MEMBERS_AstParseTypeDType;
AstNodeDType* dtypep() const { return nullptr; } AstNodeDType* dtypep() const { return nullptr; }
// METHODS // METHODS
bool similarDType(AstNodeDType* samep) const override { return this == samep; } bool similarDType(AstNodeDType* samep) const override { return this == samep; }
@ -960,7 +960,7 @@ public:
refDTypep(dtp); refDTypep(dtp);
dtypep(dtp); dtypep(dtp);
} }
ASTGEN_MEMBERS_QueueDType; ASTGEN_MEMBERS_AstQueueDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -1026,7 +1026,7 @@ public:
: ASTGEN_SUPER_RefDType(fl) { : ASTGEN_SUPER_RefDType(fl) {
this->typeofp(typeofp); this->typeofp(typeofp);
} }
ASTGEN_MEMBERS_RefDType; ASTGEN_MEMBERS_AstRefDType;
// METHODS // METHODS
const char* broken() const override; const char* broken() const override;
void cloneRelink() override; void cloneRelink() override;
@ -1101,7 +1101,7 @@ public:
refDTypep(nullptr); refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
} }
ASTGEN_MEMBERS_UnsizedArrayDType; ASTGEN_MEMBERS_AstUnsizedArrayDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -1134,7 +1134,7 @@ public:
: ASTGEN_SUPER_VoidDType(fl) { : ASTGEN_SUPER_VoidDType(fl) {
dtypep(this); dtypep(this);
} }
ASTGEN_MEMBERS_VoidDType; ASTGEN_MEMBERS_AstVoidDType;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; } bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; } bool maybePointedTo() const override { return true; }
@ -1166,7 +1166,7 @@ public:
refDTypep(nullptr); refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve dtypep(nullptr); // V3Width will resolve
} }
ASTGEN_MEMBERS_WildcardArrayDType; ASTGEN_MEMBERS_AstWildcardArrayDType;
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); || (!m_refDTypep && childDTypep())));
@ -1199,7 +1199,7 @@ class AstPackArrayDType final : public AstNodeArrayDType {
public: public:
inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep); inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep); inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
ASTGEN_MEMBERS_PackArrayDType; ASTGEN_MEMBERS_AstPackArrayDType;
string prettyDTypeName() const override; string prettyDTypeName() const override;
bool isCompound() const override { return false; } bool isCompound() const override { return false; }
}; };
@ -1226,7 +1226,7 @@ public:
// width and signing from the subDType/base type // width and signing from the subDType/base type
widthFromSub(subDTypep()); widthFromSub(subDTypep());
} }
ASTGEN_MEMBERS_UnpackArrayDType; ASTGEN_MEMBERS_AstUnpackArrayDType;
string prettyDTypeName() const override; string prettyDTypeName() const override;
bool same(const AstNode* samep) const override { bool same(const AstNode* samep) const override {
const AstUnpackArrayDType* const sp = static_cast<const AstUnpackArrayDType*>(samep); const AstUnpackArrayDType* const sp = static_cast<const AstUnpackArrayDType*>(samep);
@ -1244,7 +1244,7 @@ public:
// VSigning below is mispurposed to indicate if packed or not // VSigning below is mispurposed to indicate if packed or not
AstStructDType(FileLine* fl, VSigning numericUnpack) AstStructDType(FileLine* fl, VSigning numericUnpack)
: ASTGEN_SUPER_StructDType(fl, numericUnpack) {} : ASTGEN_SUPER_StructDType(fl, numericUnpack) {}
ASTGEN_MEMBERS_StructDType; ASTGEN_MEMBERS_AstStructDType;
string verilogKwd() const override { return "struct"; } string verilogKwd() const override { return "struct"; }
}; };
class AstUnionDType final : public AstNodeUOrStructDType { class AstUnionDType final : public AstNodeUOrStructDType {
@ -1253,7 +1253,7 @@ public:
// VSigning below is mispurposed to indicate if packed or not // VSigning below is mispurposed to indicate if packed or not
AstUnionDType(FileLine* fl, VSigning numericUnpack) AstUnionDType(FileLine* fl, VSigning numericUnpack)
: ASTGEN_SUPER_UnionDType(fl, numericUnpack) {} : ASTGEN_SUPER_UnionDType(fl, numericUnpack) {}
ASTGEN_MEMBERS_UnionDType; ASTGEN_MEMBERS_AstUnionDType;
string verilogKwd() const override { return "union"; } 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 // The extracted cyclic components
std::vector<std::unique_ptr<DfgGraph>> m_components; std::vector<std::unique_ptr<DfgGraph>> m_components;
// Map from 'variable vertex' -> 'component index' -> 'clone in that component' // Map from 'variable vertex' -> 'component index' -> 'clone in that component'
std::unordered_map<const DfgVertexLValue*, std::unordered_map<size_t, DfgVertexLValue*>> std::unordered_map<const DfgVertexVar*, std::unordered_map<size_t, DfgVertexVar*>> m_clones;
m_clones;
// METHODS // METHODS
@ -285,7 +284,7 @@ class ExtractCyclicComponents final {
void visitMergeSCCs(const DfgVertex& vtx, size_t targetComponent) { void visitMergeSCCs(const DfgVertex& vtx, size_t targetComponent) {
// We stop at variable boundaries, which is where we will split the graphs // 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 // Mark visited/move on if already visited
if (!m_merged.insert(&vtx).second) return; if (!m_merged.insert(&vtx).second) return;
@ -312,9 +311,9 @@ class ExtractCyclicComponents final {
// Methods for extraction // Methods for extraction
// Retrieve clone of vertex in the given component // 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"); 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) { if (!clonep) {
DfgGraph& dfg = component == 0 ? m_dfg : *m_components[component - 1]; DfgGraph& dfg = component == 0 ? m_dfg : *m_components[component - 1];
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) { if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
@ -322,7 +321,7 @@ class ExtractCyclicComponents final {
} else if (DfgVarArray* const aVtxp = vtx.cast<DfgVarArray>()) { } else if (DfgVarArray* const aVtxp = vtx.cast<DfgVarArray>()) {
clonep = new DfgVarArray{dfg, aVtxp->varp()}; 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)) { if (VL_UNLIKELY(m_doExpensiveChecks)) {
// Assign component number of clone for later checks // Assign component number of clone for later checks
m_state m_state
@ -338,30 +337,30 @@ class ExtractCyclicComponents final {
return *clonep; 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 // using the provided 'relink' callback
template <typename T_Vertex> template <typename T_Vertex>
void fixSources(T_Vertex& vtx, std::function<void(T_Vertex&, DfgVertex&, size_t)> relink) { void fixSources(T_Vertex& vtx, std::function<void(T_Vertex&, DfgVertex&, size_t)> relink) {
static_assert(std::is_base_of<DfgVertexLValue, T_Vertex>::value, static_assert(std::is_base_of<DfgVertexVar, T_Vertex>::value,
"'Vertex' must be a 'DfgVertexLValue'"); "'Vertex' must be a 'DfgVertexVar'");
const size_t component = m_state.at(&vtx).component; const size_t component = m_state.at(&vtx).component;
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t idx) { vtx.forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
DfgVertex& source = *edge.sourcep(); DfgVertex& source = *edge.sourcep();
// DfgVertexLValue sources are fixed up by `fixSinks` on those sources // DfgVertexVar sources are fixed up by `fixSinks` on those sources
if (source.is<DfgVarPacked>() || source.is<DfgVarArray>()) return; if (source.is<DfgVertexVar>()) return;
const size_t sourceComponent = m_state.at(&source).component; const size_t sourceComponent = m_state.at(&source).component;
// Same component is OK // Same component is OK
if (sourceComponent == component) return; if (sourceComponent == component) return;
// Unlink the source edge (source is reconnected by 'relink' // Unlink the source edge (source is reconnected by 'relink'
edge.unlinkSource(); edge.unlinkSource();
// Apply the fixup // Apply the fixup
DfgVertexLValue& clone = getClone(vtx, sourceComponent); DfgVertexVar& clone = getClone(vtx, sourceComponent);
relink(*(clone.as<T_Vertex>()), source, idx); relink(*(clone.as<T_Vertex>()), source, idx);
}); });
} }
// Fix up sinks of given variable vertex that are in a different component // 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; const size_t component = m_state.at(&vtx).component;
vtx.forEachSinkEdge([&](DfgEdge& edge) { vtx.forEachSinkEdge([&](DfgEdge& edge) {
const size_t sinkComponent = m_state.at(edge.sinkp()).component; const size_t sinkComponent = m_state.at(edge.sinkp()).component;
@ -400,7 +399,7 @@ class ExtractCyclicComponents final {
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) { vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) {
DfgVertex& source = *edge.sourcep(); DfgVertex& source = *edge.sourcep();
// OK to cross at variables // 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, UASSERT_OBJ(component == m_state.at(&source).component, &vtx,
"Component crossing edge without variable involvement"); "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) { // vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { //
if (edge.sourcep()) { if (edge.sourcep()) {
string headLabel; string headLabel;
if (vtx.arity() > 1 || vtx.is<DfgVarPacked>() || vtx.is<DfgVarArray>()) { if (vtx.arity() > 1 || vtx.is<DfgVertexVar>()) headLabel = vtx.srcName(idx);
headLabel = vtx.srcName(idx);
}
dumpDotEdge(os, edge, headLabel); dumpDotEdge(os, edge, headLabel);
} }
}); });
@ -843,7 +840,7 @@ void DfgEdge::relinkSource(DfgVertex* newSourcep) {
// DfgVertex // 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_filelinep{flp}
, m_dtypep{dtypep} , m_dtypep{dtypep}
, m_type{type} { , m_type{type} {
@ -897,7 +894,7 @@ V3Hash DfgVertex::hash(HashCache& cache) const {
result += selfHash(); result += selfHash();
// Variables are defined by themselves, so there is no need to hash the sources. This // 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. // 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); }); forEachSource([&result, &cache](const DfgVertex& src) { result += src.hash(cache); });
} }
} }
@ -930,7 +927,6 @@ void DfgVertex::replaceWith(DfgVertex* newSorucep) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DfgVarPacked ---------- // DfgVarPacked ----------
void DfgVarPacked::accept(DfgVisitor& visitor) { visitor.visit(this); }
bool DfgVarPacked::selfEquals(const DfgVertex& that) const { bool DfgVarPacked::selfEquals(const DfgVertex& that) const {
if (const DfgVarPacked* otherp = that.cast<DfgVarPacked>()) { 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()); } V3Hash DfgVarPacked::selfHash() const { return V3Hasher::uncachedHash(varp()); }
// DfgVarPacked ---------- // DfgVarPacked ----------
void DfgVarArray::accept(DfgVisitor& visitor) { visitor.visit(this); }
bool DfgVarArray::selfEquals(const DfgVertex& that) const { bool DfgVarArray::selfEquals(const DfgVertex& that) const {
if (const DfgVarArray* otherp = that.cast<DfgVarArray>()) { 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()); } V3Hash DfgVarArray::selfHash() const { return V3Hasher::uncachedHash(varp()); }
// DfgConst ---------- // DfgConst ----------
void DfgConst::accept(DfgVisitor& visitor) { visitor.visit(this); }
bool DfgConst::selfEquals(const DfgVertex& that) const { bool DfgConst::selfEquals(const DfgVertex& that) const {
if (const DfgConst* otherp = that.cast<DfgConst>()) { if (const DfgConst* otherp = that.cast<DfgConst>()) {
@ -971,12 +965,4 @@ V3Hash DfgConst::selfHash() const { return V3Hasher::uncachedHash(m_constp); }
// DfgVisitor // DfgVisitor
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void DfgVisitor::visit(DfgVarPacked* vtxp) { visit(static_cast<DfgVertex*>(vtxp)); } #include "V3Dfg__gen_visitor_defns.h" // From ./astgen
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"

View File

@ -21,7 +21,7 @@
// than the linked list based structures used by V3Graph. // than the linked list based structures used by V3Graph.
// //
// A bulk of the DfgVertex sub-types are generated by astgen, and are // 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 // See also the internals documentation docs/internals.rst
// //
@ -39,6 +39,8 @@
#include "V3Hasher.h" #include "V3Hasher.h"
#include "V3List.h" #include "V3List.h"
#include "V3Dfg__gen_forward_class_decls.h" // From ./astgen
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <functional> #include <functional>
@ -46,7 +48,10 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #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 DfgEdge;
class DfgVisitor; 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 // Dataflow graph
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -191,9 +215,6 @@ public:
// Dataflow graph vertex // Dataflow graph vertex
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Reuse the generated type constants
using DfgType = VNType;
// Base data flow graph vertex // Base data flow graph vertex
class DfgVertex VL_NOT_FINAL { class DfgVertex VL_NOT_FINAL {
friend class DfgGraph; friend class DfgGraph;
@ -206,10 +227,10 @@ protected:
DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex
FileLine* const m_filelinep; // Source location FileLine* const m_filelinep; // Source location
AstNodeDType* m_dtypep = nullptr; // Data type of the result of this vertex AstNodeDType* m_dtypep = nullptr; // Data type of the result of this vertex
const DfgType m_type; const VDfgType m_type;
// CONSTRUCTOR // CONSTRUCTOR
DfgVertex(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep, DfgType type); DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep);
public: public:
virtual ~DfgVertex(); virtual ~DfgVertex();
@ -277,7 +298,7 @@ public:
// The data type of the result of the nodes // The data type of the result of the nodes
AstNodeDType* dtypep() const { return m_dtypep; } AstNodeDType* dtypep() const { return m_dtypep; }
// The type of this vertex // The type of this vertex
DfgType type() const { return m_type; } VDfgType type() const { return m_type; }
// Width of result // Width of result
uint32_t width() const { uint32_t width() const {
@ -396,11 +417,18 @@ public:
string warnMore() const { return fileline()->warnMore(); } string warnMore() const { return fileline()->warnMore(); }
string warnOther() const { return fileline()->warnOther(); } 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 // Subtype test
template <typename T> template <typename T>
bool is() const { bool is() const {
static_assert(std::is_base_of<DfgVertex, T>::value, "'T' must be a subtype of DfgVertex"); 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 // Ensure subtype, then cast to that type
@ -436,367 +464,8 @@ public:
virtual const string srcName(size_t idx) const = 0; virtual const string srcName(size_t idx) const = 0;
}; };
// DfgVertices are, well ... DfgVertices // Specialisations of privateTypeTest
template <> #include "V3Dfg__gen_type_tests.h" // From ./astgen
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"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Dfg vertex visitor // Dfg vertex visitor
@ -807,10 +476,8 @@ public:
// Dispatch to most specific 'visit' method on 'vtxp' // Dispatch to most specific 'visit' method on 'vtxp'
void iterate(DfgVertex* vtxp) { vtxp->accept(*this); } void iterate(DfgVertex* vtxp) { vtxp->accept(*this); }
virtual void visit(DfgVarPacked* vtxp); virtual void visit(DfgVertex* nodep) = 0;
virtual void visit(DfgVarArray* vtxp); #include "V3Dfg__gen_visitor_decls.h" // From ./astgen
virtual void visit(DfgConst* vtxp);
#include "V3Dfg__gen_visitor_decls.h"
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -937,6 +604,180 @@ Vertex* DfgVertex::findSink() const {
return findSink<Vertex>([](const Vertex&) { return true; }); 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 { bool DfgVertex::isZero() const {
if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero(); if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero();
return false; return false;

View File

@ -41,8 +41,8 @@ namespace {
// Create a DfgVertex out of a AstNodeMath. For most AstNodeMath subtypes, this can be done // 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 // automatically. For the few special cases, we provide specializations below
template <typename Vertex> template <typename Vertex, typename Node>
Vertex* makeVertex(const AstForDfg<Vertex>* nodep, DfgGraph& dfg) { Vertex* makeVertex(const Node* nodep, DfgGraph& dfg) {
return new Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)}; return new Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
} }
@ -51,22 +51,22 @@ Vertex* makeVertex(const AstForDfg<Vertex>* nodep, DfgGraph& dfg) {
// LCOV_EXCL_START // LCOV_EXCL_START
// AstCCast changes width, but should not exists where DFG optimization is currently invoked // AstCCast changes width, but should not exists where DFG optimization is currently invoked
template <> template <>
DfgCCast* makeVertex<DfgCCast>(const AstCCast*, DfgGraph&) { DfgCCast* makeVertex<DfgCCast, AstCCast>(const AstCCast*, DfgGraph&) {
return nullptr; return nullptr;
} }
// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway // Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway
template <> template <>
DfgAtoN* makeVertex<DfgAtoN>(const AstAtoN*, DfgGraph&) { DfgAtoN* makeVertex<DfgAtoN, AstAtoN>(const AstAtoN*, DfgGraph&) {
return nullptr; return nullptr;
} }
// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway // Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway
template <> template <>
DfgCompareNN* makeVertex<DfgCompareNN>(const AstCompareNN*, DfgGraph&) { DfgCompareNN* makeVertex<DfgCompareNN, AstCompareNN>(const AstCompareNN*, DfgGraph&) {
return nullptr; return nullptr;
} }
// Unhandled in DfgToAst, but also operates on unpacked arrays which we don't optimize anyway // Unhandled in DfgToAst, but also operates on unpacked arrays which we don't optimize anyway
template <> template <>
DfgSliceSel* makeVertex<DfgSliceSel>(const AstSliceSel*, DfgGraph&) { DfgSliceSel* makeVertex<DfgSliceSel, AstSliceSel>(const AstSliceSel*, DfgGraph&) {
return nullptr; return nullptr;
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
@ -105,11 +105,11 @@ class AstToDfgVisitor final : public VNVisitor {
m_uncommittedVertices.clear(); m_uncommittedVertices.clear();
} }
DfgVertexLValue* getNet(AstVar* varp) { DfgVertexVar* getNet(AstVar* varp) {
if (!varp->user1p()) { 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 // 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. // created. We will delete unconnected variable vertices at the end.
if (VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) { if (VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, varp}; DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, varp};
@ -122,7 +122,7 @@ class AstToDfgVisitor final : public VNVisitor {
varp->user1p(vtxp); varp->user1p(vtxp);
} }
} }
return varp->user1u().to<DfgVertexLValue*>(); return varp->user1u().to<DfgVertexVar*>();
} }
DfgVertex* getVertex(AstNode* nodep) { 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 // 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 // automatically. For the few special cases, we provide specializations below
template <typename Node, typename... Ops> template <typename Node, typename Vertex, typename... Ops>
Node* makeNode(const DfgForAst<Node>* vtxp, Ops... ops) { Node* makeNode(const Vertex* vtxp, Ops... ops) {
Node* const nodep = new Node{vtxp->fileline(), ops...}; Node* const nodep = new Node{vtxp->fileline(), ops...};
UASSERT_OBJ(nodep->width() == static_cast<int>(vtxp->width()), vtxp, UASSERT_OBJ(nodep->width() == static_cast<int>(vtxp->width()), vtxp,
"Incorrect width in AstNode created from DfgVertex " "Incorrect width in AstNode created from DfgVertex "
@ -55,31 +55,31 @@ Node* makeNode(const DfgForAst<Node>* vtxp, Ops... ops) {
// Vertices needing special conversion // Vertices needing special conversion
template <> template <>
AstExtend* makeNode<AstExtend, AstNodeMath*>( // AstExtend* makeNode<AstExtend, DfgExtend, AstNodeMath*>( //
const DfgExtend* vtxp, AstNodeMath* op1) { const DfgExtend* vtxp, AstNodeMath* op1) {
return new AstExtend{vtxp->fileline(), op1, static_cast<int>(vtxp->width())}; return new AstExtend{vtxp->fileline(), op1, static_cast<int>(vtxp->width())};
} }
template <> template <>
AstExtendS* makeNode<AstExtendS, AstNodeMath*>( // AstExtendS* makeNode<AstExtendS, DfgExtendS, AstNodeMath*>( //
const DfgExtendS* vtxp, AstNodeMath* op1) { const DfgExtendS* vtxp, AstNodeMath* op1) {
return new AstExtendS{vtxp->fileline(), op1, static_cast<int>(vtxp->width())}; return new AstExtendS{vtxp->fileline(), op1, static_cast<int>(vtxp->width())};
} }
template <> template <>
AstShiftL* makeNode<AstShiftL, AstNodeMath*, AstNodeMath*>( // AstShiftL* makeNode<AstShiftL, DfgShiftL, AstNodeMath*, AstNodeMath*>( //
const DfgShiftL* vtxp, AstNodeMath* op1, AstNodeMath* op2) { const DfgShiftL* vtxp, AstNodeMath* op1, AstNodeMath* op2) {
return new AstShiftL{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())}; return new AstShiftL{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())};
} }
template <> template <>
AstShiftR* makeNode<AstShiftR, AstNodeMath*, AstNodeMath*>( // AstShiftR* makeNode<AstShiftR, DfgShiftR, AstNodeMath*, AstNodeMath*>( //
const DfgShiftR* vtxp, AstNodeMath* op1, AstNodeMath* op2) { const DfgShiftR* vtxp, AstNodeMath* op1, AstNodeMath* op2) {
return new AstShiftR{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())}; return new AstShiftR{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())};
} }
template <> template <>
AstShiftRS* makeNode<AstShiftRS, AstNodeMath*, AstNodeMath*>( // AstShiftRS* makeNode<AstShiftRS, DfgShiftRS, AstNodeMath*, AstNodeMath*>( //
const DfgShiftRS* vtxp, AstNodeMath* op1, AstNodeMath* op2) { const DfgShiftRS* vtxp, AstNodeMath* op1, AstNodeMath* op2) {
return new AstShiftRS{vtxp->fileline(), op1, op2, static_cast<int>(vtxp->width())}; 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 // Currently unhandled nodes - see corresponding AstToDfg functions
// LCOV_EXCL_START // LCOV_EXCL_START
template <> template <>
AstCCast* makeNode<AstCCast, AstNodeMath*>(const DfgCCast* vtxp, AstNodeMath*) { AstCCast* makeNode<AstCCast, DfgCCast, AstNodeMath*>(const DfgCCast* vtxp, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented"); vtxp->v3fatalSrc("not implemented");
} }
template <> template <>
AstAtoN* makeNode<AstAtoN, AstNodeMath*>(const DfgAtoN* vtxp, AstNodeMath*) { AstAtoN* makeNode<AstAtoN, DfgAtoN, AstNodeMath*>(const DfgAtoN* vtxp, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented"); vtxp->v3fatalSrc("not implemented");
} }
template <> template <>
AstCompareNN* makeNode<AstCompareNN, AstNodeMath*, AstNodeMath*>(const DfgCompareNN* vtxp, AstCompareNN*
makeNode<AstCompareNN, DfgCompareNN, AstNodeMath*, AstNodeMath*>(const DfgCompareNN* vtxp,
AstNodeMath*, AstNodeMath*) { AstNodeMath*, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented"); vtxp->v3fatalSrc("not implemented");
} }
template <> template <>
AstSliceSel* makeNode<AstSliceSel, AstNodeMath*, AstNodeMath*, AstNodeMath*>( AstSliceSel* makeNode<AstSliceSel, DfgSliceSel, AstNodeMath*, AstNodeMath*, AstNodeMath*>(
const DfgSliceSel* vtxp, AstNodeMath*, AstNodeMath*, AstNodeMath*) { const DfgSliceSel* vtxp, AstNodeMath*, AstNodeMath*, AstNodeMath*) {
vtxp->v3fatalSrc("not implemented"); vtxp->v3fatalSrc("not implemented");
} }
@ -215,8 +216,7 @@ class DfgToAstVisitor final : DfgVisitor {
// Inline vertices that drive only a single node, or are special // Inline vertices that drive only a single node, or are special
if (!vtx.hasMultipleSinks()) return true; if (!vtx.hasMultipleSinks()) return true;
if (vtx.is<DfgConst>()) return true; if (vtx.is<DfgConst>()) return true;
if (vtx.is<DfgVarPacked>()) return true; if (vtx.is<DfgVertexVar>()) return true;
if (vtx.is<DfgVarArray>()) return true;
if (const DfgArraySel* const selp = vtx.cast<DfgArraySel>()) { if (const DfgArraySel* const selp = vtx.cast<DfgArraySel>()) {
return selp->bitp()->is<DfgConst>(); return selp->bitp()->is<DfgConst>();
} }

View File

@ -154,7 +154,7 @@ void V3DfgPasses::removeVars(DfgGraph& dfg, DfgRemoveVarsContext& ctx) {
void V3DfgPasses::removeUnused(DfgGraph& dfg) { void V3DfgPasses::removeUnused(DfgGraph& dfg) {
const auto processVertex = [&](DfgVertex& vtx) { const auto processVertex = [&](DfgVertex& vtx) {
// Keep variables // Keep variables
if (vtx.is<DfgVarPacked>() || vtx.is<DfgVarArray>()) return false; if (vtx.is<DfgVertexVar>()) return false;
// Keep if it has sinks // Keep if it has sinks
if (vtx.hasSinks()) return false; if (vtx.hasSinks()) return false;
// Unlink and delete vertex // 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); } DfgConst* makeZero(FileLine* flp, uint32_t width) { return makeConst(flp, width, 0); }
// Transformations that apply to all commutative binary vertices // 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 lhsp = vtxp->source<0>();
DfgVertex* const rhsp = vtxp->source<1>(); DfgVertex* const rhsp = vtxp->source<1>();
// Ensure Const is on left-hand side to simplify other patterns // 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 // 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'. // CSE to later merge 'a op b' with 'b op a'.
if (lhsp->is<DfgVarPacked>() && rhsp->is<DfgVarPacked>()) { if (lhsp->is<DfgVertexVar>() && rhsp->is<DfgVertexVar>()) {
AstVar* const lVarp = lhsp->as<DfgVarPacked>()->varp(); AstVar* const lVarp = lhsp->as<DfgVertexVar>()->varp();
AstVar* const rVarp = rhsp->as<DfgVarPacked>()->varp(); AstVar* const rVarp = rhsp->as<DfgVertexVar>()->varp();
if (lVarp->name() > rVarp->name()) { if (lVarp->name() > rVarp->name()) {
APPLYING(SWAP_VAR_IN_COMMUTATIVE_BINARY) { APPLYING(SWAP_VAR_IN_COMMUTATIVE_BINARY) {
vtxp->lhsp(rhsp); vtxp->lhsp(rhsp);
@ -152,12 +152,12 @@ class V3DfgPeephole final : public DfgVisitor {
} }
// Transformations that apply to all associative binary vertices // Transformations that apply to all associative binary vertices
void associativeBinary(DfgVertexWithArity<2>* vtxp) { void associativeBinary(DfgVertexBinary* vtxp) {
DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const lhsp = vtxp->lhsp();
// Make associative trees right leaning (for better CSE opportunities) // Make associative trees right leaning (for better CSE opportunities)
if (lhsp->type() == vtxp->type() && !lhsp->hasMultipleSinks()) { 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) { APPLYING(RIGHT_LEANING_ASSOC) {
vtxp->replaceWith(lBinp); vtxp->replaceWith(lBinp);
vtxp->lhsp(lBinp->rhsp()); vtxp->lhsp(lBinp->rhsp());
@ -242,7 +242,7 @@ class V3DfgPeephole final : public DfgVisitor {
newRhsp->rhsp(concatp->rhsp()); newRhsp->rhsp(concatp->rhsp());
// The replacement Vertex // The replacement Vertex
DfgVertexWithArity<2>* const replacementp DfgVertexBinary* const replacementp
= std::is_same<Vertex, DfgEq>::value = std::is_same<Vertex, DfgEq>::value
? new DfgAnd{m_dfg, concatp->fileline(), m_bitDType} ? new DfgAnd{m_dfg, concatp->fileline(), m_bitDType}
: nullptr; : nullptr;
@ -334,7 +334,7 @@ class V3DfgPeephole final : public DfgVisitor {
rReducep->srcp(concatp->rhsp()); rReducep->srcp(concatp->rhsp());
// Bitwise reduce the results // 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->lhsp(lReducep);
replacementp->rhsp(rReducep); replacementp->rhsp(rReducep);
vtxp->replaceWith(replacementp); 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 (const DfgConcat* const concatp = vtxp->rhsp()->cast<DfgConcat>()) {
if (concatp->lhsp()->isZero()) { // Drop redundant zero extension if (concatp->lhsp()->isZero()) { // Drop redundant zero extension
APPLYING(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) { // 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 // Process one vertex. Return true if graph changed
bool processVertex(DfgVertex& vtx) { 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 // 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 // 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 // debugging to keep the original variable name, if one is available), so we can't remove
// redundant variables here. // 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 it has no sinks (unused), we can remove it
if (!keep && !vtx.hasSinks()) { 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 re
import sys import sys
import textwrap import textwrap
# from pprint import pprint, pformat # from pprint import pprint, pformat
# This class is used to represents both AstNode and DfgVertex sub-types
class Node: class Node:
def __init__(self, name, superClass, file, lineno): def __init__(self, name, superClass, file=None, lineno=None):
self._name = name self._name = name
self._superClass = superClass self._superClass = superClass
self._subClasses = [] # Initially list, but tuple after completion self._subClasses = [] # Initially list, but tuple after completion
@ -173,9 +175,11 @@ class Node:
return self in other.allSubClasses return self in other.allSubClasses
Nodes = {} AstNodes = {}
SortedNodes = None AstNodeList = None
DfgVertices = None
DfgVertices = {}
DfgVertexList = None
ClassRefs = {} ClassRefs = {}
Stages = {} Stages = {}
@ -278,7 +282,7 @@ class Cpt:
self.error("Can't parse from function: " + func) self.error("Can't parse from function: " + func)
typen = match.group(1) typen = match.group(1)
subnodes = match.group(2) subnodes = match.group(2)
if Nodes[typen].isRoot: if AstNodes[typen].isRoot:
self.error("Unknown AstNode typen: " + typen + ": in " + func) self.error("Unknown AstNode typen: " + typen + ": in " + func)
mif = "" mif = ""
@ -333,7 +337,7 @@ class Cpt:
elif match_skip: elif match_skip:
typen = match_skip.group(1) typen = match_skip.group(1)
self.tree_skip_visit[typen] = 1 self.tree_skip_visit[typen] = 1
if typen not in Nodes: if typen not in AstNodes:
self.error("Unknown node type: " + typen) self.error("Unknown node type: " + typen)
else: else:
@ -463,7 +467,7 @@ class Cpt:
self.print( self.print(
" // Bottom class up, as more simple transforms are generally better\n" " // 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_sc = []
out_for_type = [] out_for_type = []
classes = list(node.allSuperClasses) classes = list(node.allSuperClasses)
@ -502,7 +506,7 @@ class Cpt:
"".join(out_for_type_sc)) "".join(out_for_type_sc))
if out_for_type[0]: if out_for_type[0]:
self.print(" iterateAndNextNull(nodep->rhsp());\n") self.print(" iterateAndNextNull(nodep->rhsp());\n")
if node.isSubClassOf(Nodes["NodeTriop"]): if node.isSubClassOf(AstNodes["NodeTriop"]):
self.print( self.print(
" iterateAndNextNull(nodep->thsp());\n") " iterateAndNextNull(nodep->thsp());\n")
self.print("".join(out_for_type) + " }\n") self.print("".join(out_for_type) + " }\n")
@ -542,7 +546,7 @@ def parseOpType(string):
return None return None
def read_types(filename): def read_types(filename, Nodes, prefix):
hasErrors = False hasErrors = False
def error(lineno, message): def error(lineno, message):
@ -560,8 +564,9 @@ def read_types(filename):
return return
if not hasAstgenMembers: if not hasAstgenMembers:
error( error(
node.lineno, "'Ast" + node.name + node.lineno,
"' does not contain 'ASTGEN_MEMBERS_" + node.name + ";'") "'{p}{n}' does not contain 'ASTGEN_MEMBERS_{p}{n};'".format(
p=prefix, n=node.name))
hasAstgenMembers = False hasAstgenMembers = False
with open(filename) as fh: with open(filename) as fh:
@ -575,12 +580,12 @@ def read_types(filename):
classn = match.group(2) classn = match.group(2)
match = re.search(r':\s*public\s+(\S+)', line) match = re.search(r':\s*public\s+(\S+)', line)
supern = match.group(1) if match else "" supern = match.group(1) if match else ""
if re.search(r'Ast', supern): if re.search(prefix, supern):
classn = re.sub(r'^Ast', '', classn) classn = re.sub(r'^' + prefix, '', classn)
supern = re.sub(r'^Ast', '', supern) supern = re.sub(r'^' + prefix, '', supern)
if not supern: if not supern:
sys.exit("%Error: 'Ast{}' has no super-class".format( sys.exit("%Error: '{p}{c}' has no super-class".format(
classn)) p=prefix, c=classn))
checkFinishedNode(node) checkFinishedNode(node)
superClass = Nodes[supern] superClass = Nodes[supern]
node = Node(classn, superClass, filename, lineno) node = Node(classn, superClass, filename, lineno)
@ -589,8 +594,13 @@ def read_types(filename):
if not node: if not node:
continue continue
if re.match(r'^\s*ASTGEN_MEMBERS_' + node.name + ';', line): if re.match(r'^\s*ASTGEN_MEMBERS_' + prefix + node.name + ';',
line):
hasAstgenMembers = True hasAstgenMembers = True
if prefix != "Ast":
continue
match = re.match(r'^\s*//\s*@astgen\s+(.*)$', line) match = re.match(r'^\s*//\s*@astgen\s+(.*)$', line)
if match: if match:
decl = re.sub(r'//.*$', '', match.group(1)) decl = re.sub(r'//.*$', '', match.group(1))
@ -650,6 +660,59 @@ def read_types(filename):
sys.exit("%Error: Stopping due to errors reported above") 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): def read_stages(filename):
with open(filename) as fh: with open(filename) as fh:
n = 100 n = 100
@ -712,7 +775,7 @@ def write_report(filename):
fh.write(" " + classn + "\n") fh.write(" " + classn + "\n")
fh.write("\nClasses:\n") fh.write("\nClasses:\n")
for node in SortedNodes: for node in AstNodeList:
fh.write(" class Ast%-17s\n" % node.name) fh.write(" class Ast%-17s\n" % node.name)
fh.write(" arity: {}\n".format(node.arity)) fh.write(" arity: {}\n".format(node.arity))
fh.write(" parent: ") fh.write(" parent: ")
@ -741,97 +804,110 @@ def write_report(filename):
fh.write("\n") fh.write("\n")
def write_classes(filename): ################################################################################
with open_file(filename) as fh: # Common code genaration
fh.write("class AstNode;\n") ################################################################################
for node in SortedNodes:
fh.write("class Ast%-17s // " % (node.name + ";"))
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: 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") fh.write("\n")
def write_visitor_decls(filename): def write_visitor_decls(prefix, nodeList):
with open_file(filename) as fh: with open_file("V3{p}__gen_visitor_decls.h".format(p=prefix)) as fh:
for node in SortedNodes: for node in nodeList:
if not node.isRoot: 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): def write_visitor_defns(prefix, nodeList, visitor):
with open_file(filename) as fh: with open_file("V3{p}__gen_visitor_defns.h".format(p=prefix)) as fh:
for node in SortedNodes: variable = "nodep" if prefix == "Ast" else "vtxp"
for node in nodeList:
base = node.superClass base = node.superClass
if base is not None: 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( fh.write(
"static_cast<int>(nodep->type()) >= static_cast<int>(VNType::first" "void {c}::visit({p}{n}* {v}) {{ visit(static_cast<{p}{b}*>({v})); }}\n"
+ node.name + ") && ") .format(c=visitor,
fh.write( p=prefix,
"static_cast<int>(nodep->type()) <= static_cast<int>(VNType::last" n=node.name,
+ node.name + "); ") b=base.name,
else: v=variable))
fh.write("nodep->type() == VNType::at" + node.name + "; ")
fh.write("}\n")
def write_types(filename): def write_type_enum(prefix, nodeList):
with open_file(filename) as fh: 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") 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): key=lambda _: _.typeId):
fh.write(" at" + node.name + " = " + str(node.typeId) + fh.write(" at{t} = {n},\n".format(t=node.name,
",\n") n=node.typeId))
fh.write(" _ENUM_END = " + str(Nodes["Node"].typeIdMax + 1) + fh.write(" _ENUM_END = {n}\n".format(n=root.typeIdMax + 1))
"\n")
fh.write(" };\n") fh.write(" };\n")
fh.write(" enum bounds : uint16_t {\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): key=lambda _: _.typeIdMin):
fh.write(" first" + node.name + " = " + fh.write(" first{t} = {n},\n".format(t=node.name,
str(node.typeIdMin) + ",\n") n=node.typeIdMin))
fh.write(" last" + node.name + " = " + str(node.typeIdMax) + fh.write(" last{t} = {n},\n".format(t=node.name,
",\n") n=node.typeIdMax))
fh.write(" _BOUNDS_END\n") fh.write(" _BOUNDS_END\n")
fh.write(" };\n") fh.write(" };\n")
fh.write(" const char* ascii() const {\n") fh.write(" const char* ascii() const {\n")
fh.write(" static const char* const names[_ENUM_END + 1] = {\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): 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(" \"_ENUM_END\"\n")
fh.write(" };\n") fh.write(" };\n")
fh.write(" return names[m_e];\n") fh.write(" return names[m_e];\n")
fh.write(" }\n") fh.write(" }\n")
def write_yystype(filename): def write_type_tests(prefix, nodeList):
with open_file(filename) as fh: with open_file("V3{p}__gen_type_tests.h".format(p=prefix)) as fh:
for node in SortedNodes: fh.write("// For internal use. They assume argument is not nullptr.\n")
fh.write("Ast{t}* {m}p;\n".format(t=node.name, if prefix == "Ast":
m=node.name[0].lower() + base = "AstNode"
node.name[1:])) 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: with open_file(filename) as fh:
def emitBlock(pattern, **fmt): def emitBlock(pattern, **fmt):
@ -839,8 +915,8 @@ def write_macros(filename):
textwrap.indent(textwrap.dedent(pattern), textwrap.indent(textwrap.dedent(pattern),
" ").format(**fmt).replace("\n", " \\\n")) " ").format(**fmt).replace("\n", " \\\n"))
for node in SortedNodes: for node in AstNodeList:
fh.write("#define ASTGEN_MEMBERS_{t} \\\n".format(t=node.name)) fh.write("#define ASTGEN_MEMBERS_Ast{t} \\\n".format(t=node.name))
emitBlock('''\ emitBlock('''\
static Ast{t}* cloneTreeNull(Ast{t}* nodep, bool cloneNextLink) {{ static Ast{t}* cloneTreeNull(Ast{t}* nodep, bool cloneNextLink) {{
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr; return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
@ -860,7 +936,7 @@ def write_macros(filename):
''', ''',
t=node.name) t=node.name)
for n in (1, 2, 3, 4): for n in range(1, 5):
op = node.getOp(n) op = node.getOp(n)
if not op: if not op:
continue continue
@ -907,7 +983,15 @@ def write_macros(filename):
fh.write("\n") 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: with open_file(filename) as fh:
indent = "" indent = ""
@ -917,7 +1001,7 @@ def write_op_checks(filename):
textwrap.indent(textwrap.dedent(pattern), textwrap.indent(textwrap.dedent(pattern),
indent).format(**fmt)) indent).format(**fmt))
for node in SortedNodes: for node in AstNodeList:
if not node.isLeaf: if not node.isLeaf:
continue 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: with open_file(filename) as fh:
fh.write("\n")
for node in DfgVertices: def emitBlock(pattern, **fmt):
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")
fh.write( fh.write(
" static constexpr DfgType dfgType() {{ return DfgType::at{t}; }};\n" textwrap.indent(textwrap.dedent(pattern),
.format(t=node.name)) " ").format(**fmt).replace("\n", " \\\n"))
fh.write("public:\n")
fh.write( for node in DfgVertexList:
" Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) : DfgVertexWithArity<{a}>{{dfg, flp, dtypep, dfgType()}} {{}}\n" fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
.format(t=node.name, a=node.arity))
# Accessors 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( operandNames = tuple(
node.getOp(n)[0] for n in range(1, node.arity + 1)) 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: 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( fh.write(
" const string srcName(size_t idx) const override {\n") " static_assert(true, \"\")\n") # Swallowing the semicolon
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")
def write_dfg_visitor_decls(filename): def write_dfg_auto_classes(filename):
with open_file(filename) as fh: 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): for node in DfgVertexList:
with open_file(filename) as fh: # 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") 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): def write_dfg_ast_to_dfg(filename):
with open_file(filename) as fh: with open_file(filename) as fh:
fh.write("\n") for node in DfgVertexList:
for node in DfgVertices: # Only generate code for automatically derieved leaf nodes
if (node.file is not None) or (not node.isLeaf):
continue
fh.write( fh.write(
"void visit(Ast{t}* nodep) override {{\n".format(t=node.name)) "void visit(Ast{t}* nodep) override {{\n".format(t=node.name))
fh.write( 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( fh.write(
" Dfg{t}* const vtxp = makeVertex<Dfg{t}>(nodep, *m_dfgp);\n" " Dfg{t}* const vtxp = makeVertex<Dfg{t}>(nodep, *m_dfgp);\n"
.format(t=node.name)) .format(t=node.name))
@ -1095,24 +1175,23 @@ def write_dfg_ast_to_dfg(filename):
fh.write(" ++m_ctx.m_nonRepNode;\n") fh.write(" ++m_ctx.m_nonRepNode;\n")
fh.write(" return;\n") fh.write(" return;\n")
fh.write(" }\n\n") fh.write(" }\n\n")
fh.write(" m_uncommittedVertices.push_back(vtxp);\n")
for i in range(node.arity): 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( fh.write(
' UASSERT_OBJ(nodep->op{j}p()->user1p(), nodep, "Child {j} missing Dfg vertex");\n' " vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to<DfgVertex*>());\n"
.format(j=i + 1))
fh.write(
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user1u().to<DfgVertex*>());\n\n"
.format(i=i, j=i + 1)) .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(" nodep->user1p(vtxp);\n")
fh.write("}\n") fh.write("}\n")
def write_dfg_dfg_to_ast(filename): def write_dfg_dfg_to_ast(filename):
with open_file(filename) as fh: with open_file(filename) as fh:
fh.write("\n") for node in DfgVertexList:
for node in DfgVertices: # Only generate code for automatically derieved leaf nodes
if (node.file is not None) or (not node.isLeaf):
continue
fh.write( fh.write(
"void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name)) "void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
for i in range(node.arity): 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', parser.add_argument('--astdef',
action='append', action='append',
help='add AST definition file (relative to -I)') 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', parser.add_argument('--classes',
action='store_true', action='store_true',
help='makes class declaration files') help='makes class declaration files')
@ -1155,66 +1237,86 @@ parser.add_argument('infiles', nargs='*', help='list of input .cpp filenames')
Args = parser.parse_args() Args = parser.parse_args()
###############################################################################
# Read AstNode definitions
###############################################################################
# Set up the root AstNode type. It is standalone so we don't need to parse the # Set up the root AstNode type. It is standalone so we don't need to parse the
# sources for this. # 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: 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 # 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_types(AstNodeList, "Ast", "Node")
# Check all leaves are not AstNode* and non-leaves are AstNode*
if re.match(r'^Node', node.name): ###############################################################################
if node.isLeaf: # Read and generate DfgVertex definitions
sys.exit( ###############################################################################
"%Error: Final AstNode subclasses must not be named AstNode*: Ast"
+ node.name) # Set up the root DfgVertex type and some other hand-written base types.
else: # 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: if not node.isLeaf:
sys.exit( continue
"%Error: Non-final AstNode subclasses must be named AstNode*: Ast"
+ node.name)
DfgBases = (Nodes["NodeUniop"], Nodes["NodeBiop"], Nodes["NodeTriop"]) # Ignore any explicitly defined vertex
DfgVertices = tuple( if node.name in DfgVertices:
node for node in SortedNodes continue
if node.isLeaf and any(node.isSubClassOf(base) for base in DfgBases))
# Check ordering of node definitions if node.isSubClassOf(AstNodes["NodeUniop"]):
files = tuple(sorted(set(_.file for _ in SortedNodes))) base = DfgVertices["VertexUnary"]
elif node.isSubClassOf(AstNodes["NodeBiop"]):
base = DfgVertices["VertexBinary"]
elif node.isSubClassOf(AstNodes["NodeTriop"]):
base = DfgVertices["VertexTernary"]
else:
continue
hasOrderingError = False vertex = Node(node.name, base)
for file in files: DfgVertices[node.name] = vertex
nodes = tuple(filter(lambda _, f=file: _.file == f, SortedNodes)) base.addSubClass(vertex)
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)
if hasOrderingError: for n in range(1, node.arity + 1):
sys.exit("%Error: Stopping due to out of order definitions listed above") 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") read_stages(Args.I + "/Verilator.cpp")
@ -1224,19 +1326,29 @@ source_files.extend(glob.glob(Args.I + "/*.cpp"))
for filename in source_files: for filename in source_files:
read_refs(filename) read_refs(filename)
###############################################################################
# Generate output
###############################################################################
if Args.classes: if Args.classes:
write_report("V3Ast__gen_report.txt") write_report("V3Ast__gen_report.txt")
write_classes("V3Ast__gen_classes.h") # Write Ast code
write_visitor_decls("V3Ast__gen_visitor_decls.h") write_forward_class_decls("Ast", AstNodeList)
write_visitor_defns("V3Ast__gen_visitor_defns.h") write_visitor_decls("Ast", AstNodeList)
write_impl("V3Ast__gen_impl.h") write_visitor_defns("Ast", AstNodeList, "VNVisitor")
write_types("V3Ast__gen_types.h") write_type_enum("Ast", AstNodeList)
write_yystype("V3Ast__gen_yystype.h") write_type_tests("Ast", AstNodeList)
write_macros("V3Ast__gen_macros.h") write_ast_macros("V3Ast__gen_macros.h")
write_op_checks("V3Ast__gen_op_checks.h") write_ast_yystype("V3Ast__gen_yystype.h")
write_dfg_vertex_classes("V3Dfg__gen_vertex_classes.h") write_ast_op_checks("V3Ast__gen_op_checks.h")
write_dfg_visitor_decls("V3Dfg__gen_visitor_decls.h") # Write Dfg code
write_dfg_definitions("V3Dfg__gen_definitions.h") 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_ast_to_dfg("V3Dfg__gen_ast_to_dfg.h")
write_dfg_dfg_to_ast("V3Dfg__gen_dfg_to_ast.h") write_dfg_dfg_to_ast("V3Dfg__gen_dfg_to_ast.h")