Internals: Manage DfgVertex user data via a typed map (#6356)
Add DfgUserMap as a handle around the one pointer worth of algorithm specific 'user' storage in each DfgVertex. This reduces verbosity, improves type safety and correctness. Also enables us to remove one pointer from DfgVertex to reduce memory use. No functional change.
This commit is contained in:
parent
a6f26b85b3
commit
e63ed0a931
|
|
@ -223,20 +223,26 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
|||
}
|
||||
// Otherwise they will be moved
|
||||
vtxp->nodep()->user2p(vtxp);
|
||||
vtxp->m_userCnt = 0;
|
||||
vtxp->m_graphp = this;
|
||||
vtxp->m_userGeneration = 0;
|
||||
#ifdef VL_DEBUG
|
||||
vtxp->m_dfgp = this;
|
||||
#endif
|
||||
}
|
||||
m_varVertices.splice(m_varVertices.end(), otherp->m_varVertices);
|
||||
// Process constants
|
||||
for (DfgConst& vtx : otherp->m_constVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
vtx.m_userGeneration = 0;
|
||||
#ifdef VL_DEBUG
|
||||
vtx.m_dfgp = this;
|
||||
#endif
|
||||
}
|
||||
m_constVertices.splice(m_constVertices.end(), otherp->m_constVertices);
|
||||
// Process operations
|
||||
for (DfgVertex& vtx : otherp->m_opVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
vtx.m_userGeneration = 0;
|
||||
#ifdef VL_DEBUG
|
||||
vtx.m_dfgp = this;
|
||||
#endif
|
||||
}
|
||||
m_opVertices.splice(m_opVertices.end(), otherp->m_opVertices);
|
||||
// Update graph sizes
|
||||
|
|
@ -586,8 +592,8 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
|||
return result >> 1;
|
||||
}
|
||||
|
||||
V3Hash DfgVertex::hash() {
|
||||
V3Hash& result = user<V3Hash>();
|
||||
V3Hash DfgVertex::hash(DfgUserMap<V3Hash>& cache) {
|
||||
V3Hash& result = cache[this];
|
||||
if (!result.value()) {
|
||||
V3Hash hash{selfHash()};
|
||||
// Variables are defined by themselves, so there is no need to hash them further
|
||||
|
|
@ -597,7 +603,7 @@ V3Hash DfgVertex::hash() {
|
|||
hash += m_type;
|
||||
hash += size();
|
||||
foreachSource([&](DfgVertex& vtx) {
|
||||
hash += vtx.hash();
|
||||
hash += vtx.hash(cache);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
294
src/V3Dfg.h
294
src/V3Dfg.h
|
|
@ -54,10 +54,18 @@
|
|||
#define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE
|
||||
#endif
|
||||
|
||||
// Can T be stored in the memory allocted for U?
|
||||
template <typename T, typename U>
|
||||
inline constexpr bool fitsSpaceAllocatedFor() {
|
||||
return sizeof(T) <= sizeof(U) && alignof(T) <= alignof(U);
|
||||
}
|
||||
|
||||
class DfgEdge;
|
||||
class DfgVertex;
|
||||
class DfgGraph;
|
||||
class DfgVisitor;
|
||||
template <typename T_User, bool = fitsSpaceAllocatedFor<T_User, void*>()>
|
||||
class DfgUserMap;
|
||||
|
||||
// Specialization of std::hash for a std::pair<const DfgVertex*, const DfgVertex*> for use below
|
||||
template <>
|
||||
|
|
@ -191,8 +199,8 @@ class DfgVertex VL_NOT_FINAL {
|
|||
friend class DfgGraph;
|
||||
friend class DfgEdge;
|
||||
friend class DfgVisitor;
|
||||
|
||||
using UserDataStorage = void*; // Storage allocated for user data
|
||||
template <typename, bool>
|
||||
friend class DfgUserMap;
|
||||
|
||||
// STATE
|
||||
V3ListLinks<DfgVertex> m_links; // V3List links in the DfgGraph
|
||||
|
|
@ -201,10 +209,16 @@ class DfgVertex VL_NOT_FINAL {
|
|||
|
||||
FileLine* const m_filelinep; // Source location
|
||||
AstNodeDType* m_dtypep; // Data type of the result of this vertex - mutable for efficiency
|
||||
DfgGraph* m_graphp; // The containing DfgGraph
|
||||
const VDfgType m_type; // Vertex type tag
|
||||
uint32_t m_userCnt = 0; // User data generation number
|
||||
UserDataStorage m_userDataStorage = nullptr; // User data storage
|
||||
|
||||
// The only way to access thes is via DfgUserMap, so mutable is appropriate,
|
||||
// the map can change while the keys (DfgVertex) are const.
|
||||
mutable uint32_t m_userGeneration = 0; // User data generation number
|
||||
mutable void* m_userStorage = nullptr; // User data storage - one pointer worth
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
DfgGraph* m_dfgp = nullptr; // Graph this vertex belongs to
|
||||
#endif
|
||||
|
||||
// METHODS
|
||||
// Visitor accept method
|
||||
|
|
@ -252,8 +266,10 @@ public:
|
|||
FileLine* fileline() const { return m_filelinep; }
|
||||
// The data type of the result of the vertex
|
||||
AstNodeDType* dtypep() const { return m_dtypep; }
|
||||
// Is it a packed type (instead of an array)
|
||||
// Is it a packed type
|
||||
bool isPacked() const { return VN_IS(dtypep(), BasicDType); }
|
||||
// Is it an array type
|
||||
bool isArray() const { return !isPacked(); }
|
||||
// Width of result
|
||||
uint32_t width() const {
|
||||
UASSERT_OBJ(isPacked(), this, "non-packed has no 'width()'");
|
||||
|
|
@ -265,19 +281,6 @@ public:
|
|||
return VN_AS(dtypep(), UnpackArrayDType)->elementsConst();
|
||||
}
|
||||
|
||||
// Retrieve user data, constructing it fresh on first try.
|
||||
template <typename T_User>
|
||||
T_User& user();
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T_User>
|
||||
const T_User& getUser() const;
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T_User>
|
||||
T_User& getUser();
|
||||
// Set user data, becomes current.
|
||||
template <typename T_User>
|
||||
void setUser(T_User value);
|
||||
|
||||
// Cache type for 'equals' below
|
||||
using EqualsCache = std::unordered_map<std::pair<const DfgVertex*, const DfgVertex*>, uint8_t>;
|
||||
|
||||
|
|
@ -295,8 +298,8 @@ public:
|
|||
}
|
||||
|
||||
// Hash of vertex (depends on this vertex and all upstream vertices feeding into this vertex).
|
||||
// Uses user data for caching hashes
|
||||
V3Hash hash() VL_MT_DISABLED;
|
||||
// Uses the given DfgUserMap for caching hashes
|
||||
V3Hash hash(DfgUserMap<V3Hash>& cache) VL_MT_DISABLED;
|
||||
|
||||
// Predicate: has 1 or more sinks
|
||||
bool hasSinks() const { return !m_sinks.empty(); }
|
||||
|
|
@ -465,34 +468,7 @@ public:
|
|||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
friend class DfgUserMapBase;
|
||||
|
||||
// MEMBERS
|
||||
|
||||
|
|
@ -503,16 +479,18 @@ class DfgGraph final {
|
|||
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
|
||||
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
|
||||
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph),
|
||||
// or nullptr when run after V3Scope
|
||||
AstModule* const m_modulep;
|
||||
const std::string m_name; // Name of graph - need not be unique
|
||||
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
|
||||
|
||||
// The only way to access thes is via DfgUserMap, so mutable is appropriate,
|
||||
// the map can change while the graph is const.
|
||||
mutable bool m_vertexUserInUse = false; // Vertex user data currently in use
|
||||
mutable uint32_t m_vertexUserGeneration = 0; // Vertex user data generation counter
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule* modulep, const string& name = "") VL_MT_DISABLED;
|
||||
|
|
@ -528,14 +506,9 @@ public:
|
|||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
// Create a new DfgUserMap
|
||||
template <typename T_User>
|
||||
inline DfgUserMap<T_User> makeUserMap() const;
|
||||
|
||||
// Access to vertex lists
|
||||
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
|
||||
|
|
@ -547,6 +520,9 @@ public:
|
|||
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
void addVertex(DfgVertex& vtx) {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_OBJ(!vtx.m_dfgp, &vtx, "Vertex already in a graph");
|
||||
#endif
|
||||
// Note: changes here need to be replicated in DfgGraph::mergeGraphs
|
||||
++m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
|
|
@ -556,12 +532,17 @@ public:
|
|||
} else {
|
||||
m_opVertices.linkBack(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
vtx.m_userGeneration = 0;
|
||||
#ifdef VL_DEBUG
|
||||
vtx.m_dfgp = this;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
void removeVertex(DfgVertex& vtx) {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_OBJ(vtx.m_dfgp == this, &vtx, "Vertex not in this graph");
|
||||
#endif
|
||||
// Note: changes here need to be replicated in DfgGraph::mergeGraphs
|
||||
--m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
|
|
@ -571,8 +552,10 @@ public:
|
|||
} else {
|
||||
m_opVertices.unlink(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
vtx.m_userGeneration = 0;
|
||||
#ifdef VL_DEBUG
|
||||
vtx.m_dfgp = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
|
|
@ -655,6 +638,137 @@ public:
|
|||
sinkCone(const std::vector<const DfgVertex*>&) const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Map from DfgVertices to T_Value implemeneted via DfgVertex::m_userStorage
|
||||
|
||||
// Base class with common behavour
|
||||
class DfgUserMapBase VL_NOT_FINAL {
|
||||
template <typename, bool>
|
||||
friend class DfgUserMap;
|
||||
|
||||
protected:
|
||||
// STATE
|
||||
const DfgGraph* m_dfgp; // The graph this map is for
|
||||
// The current generation number
|
||||
const uint32_t m_currentGeneration;
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit DfgUserMapBase(const DfgGraph* dfgp)
|
||||
: m_dfgp{dfgp}
|
||||
, m_currentGeneration{++m_dfgp->m_vertexUserGeneration} {
|
||||
UASSERT(m_currentGeneration, "DfgGraph user data genartion number overflow");
|
||||
UASSERT(!m_dfgp->m_vertexUserInUse, "DfgUserMap already in use for this DfgGraph");
|
||||
m_dfgp->m_vertexUserInUse = true;
|
||||
}
|
||||
VL_UNCOPYABLE(DfgUserMapBase);
|
||||
DfgUserMapBase(DfgUserMapBase&& that)
|
||||
: m_dfgp{that.m_dfgp}
|
||||
, m_currentGeneration{that.m_currentGeneration} {
|
||||
that.m_dfgp = nullptr;
|
||||
}
|
||||
DfgUserMapBase& operator=(DfgUserMapBase&&) = delete;
|
||||
|
||||
public:
|
||||
~DfgUserMapBase() {
|
||||
if (m_dfgp) m_dfgp->m_vertexUserInUse = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization where T_Value fits in DfgVertex::m_userStorage directly
|
||||
template <typename T_Value>
|
||||
class DfgUserMap<T_Value, true> final : public DfgUserMapBase {
|
||||
static_assert(fitsSpaceAllocatedFor<T_Value, decltype(DfgVertex::m_userStorage)>(),
|
||||
"'T_Value' does not fit 'DfgVertex::m_userStorage'");
|
||||
friend class DfgGraph;
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit DfgUserMap(const DfgGraph* dfgp)
|
||||
: DfgUserMapBase{dfgp} {}
|
||||
VL_UNCOPYABLE(DfgUserMap);
|
||||
DfgUserMap& operator=(DfgUserMap&&) = delete;
|
||||
|
||||
public:
|
||||
DfgUserMap(DfgUserMap&&) = default;
|
||||
~DfgUserMap() = default;
|
||||
|
||||
// METHODS
|
||||
// Retrieve mapped value for 'vtx', value initializing it on first access
|
||||
T_Value& operator[](const DfgVertex& vtx) {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph");
|
||||
#endif
|
||||
T_Value* const storagep = reinterpret_cast<T_Value*>(&vtx.m_userStorage);
|
||||
if (vtx.m_userGeneration != m_currentGeneration) {
|
||||
new (storagep) T_Value{};
|
||||
vtx.m_userGeneration = m_currentGeneration;
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
// Same as above with pointer as key
|
||||
T_Value& operator[](const DfgVertex* vtxp) { return (*this)[*vtxp]; }
|
||||
|
||||
// Retrieve mapped value of 'vtx', must be alerady present
|
||||
T_Value& at(const DfgVertex& vtx) const {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph");
|
||||
#endif
|
||||
UASSERT_OBJ(vtx.m_userGeneration == m_currentGeneration, &vtx, "Vertex not in map");
|
||||
T_Value* const storagep = reinterpret_cast<T_Value*>(&vtx.m_userStorage);
|
||||
return *storagep;
|
||||
}
|
||||
// Same as above with pointer as key
|
||||
T_Value& at(const DfgVertex* vtxp) const { return (*this).at(*vtxp); }
|
||||
};
|
||||
|
||||
// Specialization where T_Value does not fit in DfgVertex::m_userStorage directly
|
||||
template <typename T_Value>
|
||||
class DfgUserMap<T_Value, false> final : public DfgUserMapBase {
|
||||
static_assert(fitsSpaceAllocatedFor<T_Value*, decltype(DfgVertex::m_userStorage)>(),
|
||||
"'T_Value*' does not fit 'DfgVertex::m_userStorage'");
|
||||
friend class DfgGraph;
|
||||
|
||||
// STATE
|
||||
std::deque<T_Value> m_storage; // Storage for T_Value instances
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit DfgUserMap(const DfgGraph* dfgp)
|
||||
: DfgUserMapBase{dfgp} {}
|
||||
VL_UNCOPYABLE(DfgUserMap);
|
||||
DfgUserMap& operator=(DfgUserMap&&) = delete;
|
||||
|
||||
public:
|
||||
DfgUserMap(DfgUserMap&&) = default;
|
||||
~DfgUserMap() = default;
|
||||
|
||||
// METHODS
|
||||
// Retrieve mapped value for 'vtx', value initializing it on first access
|
||||
T_Value& operator[](const DfgVertex& vtx) {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph");
|
||||
#endif
|
||||
T_Value*& storagepr = reinterpret_cast<T_Value*&>(vtx.m_userStorage);
|
||||
if (vtx.m_userGeneration != m_currentGeneration) {
|
||||
m_storage.emplace_back();
|
||||
storagepr = &m_storage.back();
|
||||
vtx.m_userGeneration = m_currentGeneration;
|
||||
}
|
||||
return *storagepr;
|
||||
}
|
||||
// Same as above with pointer as key
|
||||
T_Value& operator[](const DfgVertex* vtxp) { return (*this)[*vtxp]; }
|
||||
|
||||
// Retrieve mapped value of 'vtx', must be alerady present
|
||||
T_Value& at(const DfgVertex& vtx) const {
|
||||
#ifdef VL_DEBUG
|
||||
UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph");
|
||||
#endif
|
||||
UASSERT_OBJ(vtx.m_userGeneration == m_currentGeneration, &vtx, "Vertex not in map");
|
||||
return *reinterpret_cast<T_Value*&>(vtx.m_userStorage);
|
||||
}
|
||||
// Same as above with pointer as key
|
||||
T_Value& at(const DfgVertex* vtxp) const { return (*this).at(*vtxp); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions
|
||||
|
||||
|
|
@ -684,57 +798,11 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) {
|
|||
|
||||
// }}}
|
||||
|
||||
// DfgVertex {{{
|
||||
// DfgGraph {{{
|
||||
|
||||
template <typename T_User>
|
||||
T_User& DfgVertex::user() {
|
||||
static_assert(sizeof(T_User) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T_User' is too large for allocated storage");
|
||||
static_assert(alignof(T_User) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T_User' is larger than allocated storage");
|
||||
T_User* const storagep = reinterpret_cast<T_User*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
new (storagep) T_User{};
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
|
||||
template <typename T_User>
|
||||
const T_User& DfgVertex::getUser() const {
|
||||
static_assert(sizeof(T_User) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T_User' is too large for allocated storage");
|
||||
static_assert(alignof(T_User) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T_User' is larger than allocated storage");
|
||||
const T_User* const storagep = reinterpret_cast<const T_User*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
|
||||
template <typename T_User>
|
||||
T_User& DfgVertex::getUser() {
|
||||
return const_cast<T_User&>(const_cast<const DfgVertex*>(this)->getUser<T_User>());
|
||||
}
|
||||
|
||||
template <typename T_User>
|
||||
void DfgVertex::setUser(T_User value) {
|
||||
static_assert(sizeof(T_User) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T_User' is too large for allocated storage");
|
||||
static_assert(alignof(T_User) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T_User' is larger than allocated storage");
|
||||
T_User* const storagep = reinterpret_cast<T_User*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
DfgUserMap<T_User> DfgGraph::makeUserMap() const {
|
||||
return DfgUserMap<T_User>{this};
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
// Throughout these algorithm, we use the DfgUserMap as a map to the SCC number
|
||||
using Vtx2Scc = DfgUserMap<uint64_t>;
|
||||
|
||||
class TraceDriver final : public DfgVisitor {
|
||||
// TYPES
|
||||
|
||||
|
|
@ -63,6 +66,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map
|
||||
// The strongly connected component we are trying to escape
|
||||
const uint64_t m_component;
|
||||
const bool m_aggressive; // Trace aggressively, creating intermediate ops
|
||||
|
|
@ -110,7 +114,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
|
||||
if VL_CONSTEXPR_CXX17 (std::is_same<DfgConst, Vertex>::value) {
|
||||
DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width, 0};
|
||||
vtxp->template setUser<uint64_t>(0);
|
||||
m_vtx2Scc[vtxp] = 0;
|
||||
m_newVtxps.emplace_back(vtxp);
|
||||
return reinterpret_cast<Vertex*>(vtxp);
|
||||
} else {
|
||||
|
|
@ -121,7 +125,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
Vertex>::type;
|
||||
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
||||
Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep};
|
||||
vtxp->template setUser<uint64_t>(0);
|
||||
m_vtx2Scc[vtxp] = 0;
|
||||
m_newVtxps.emplace_back(vtxp);
|
||||
return reinterpret_cast<Vertex*>(vtxp);
|
||||
}
|
||||
|
|
@ -163,7 +167,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
// found what we were looking for. However, keep going past a splice,
|
||||
// or a unit as they cannot be use directly (they must always feed into
|
||||
// a variable, so we can't make them drive arbitrary logic)
|
||||
if (vtxp->getUser<uint64_t>() != m_component //
|
||||
if (m_vtx2Scc[vtxp] != m_component //
|
||||
&& !vtxp->is<DfgVertexSplice>() //
|
||||
&& !vtxp->is<DfgUnitArray>()) {
|
||||
if (msb != vtxp->width() - 1 || lsb != 0) {
|
||||
|
|
@ -614,8 +618,9 @@ class TraceDriver final : public DfgVisitor {
|
|||
#undef SET_RESULT
|
||||
|
||||
// CONSTRUCTOR
|
||||
TraceDriver(DfgGraph& dfg, uint64_t component, bool aggressive)
|
||||
TraceDriver(DfgGraph& dfg, Vtx2Scc& vtx2Scc, uint64_t component, bool aggressive)
|
||||
: m_dfg{dfg}
|
||||
, m_vtx2Scc{vtx2Scc}
|
||||
, m_component{component}
|
||||
, m_aggressive{aggressive} {
|
||||
#ifdef VL_DEBUG
|
||||
|
|
@ -629,18 +634,18 @@ class TraceDriver final : public DfgVisitor {
|
|||
}
|
||||
|
||||
public:
|
||||
// Given a Vertex that is part of an SCC denoted by vtxp->user<uint64_t>(),
|
||||
// Given a Vertex that is part of an SCC denoted by vtx2Scc,
|
||||
// return a vertex that is equivalent to 'vtxp[lsb +: width]', but is not
|
||||
// part of the same SCC. Returns nullptr if such a vertex cannot be
|
||||
// computed. This can add new vertices to the graph. The 'aggressive' flag
|
||||
// enables creating many intermediate operations. This should only be set
|
||||
// if it is reasonably certain the tracing will succeed, otherwise we can
|
||||
// waste a lot of compute.
|
||||
static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width,
|
||||
bool aggressive) {
|
||||
TraceDriver traceDriver{dfg, vtxp->getUser<uint64_t>(), aggressive};
|
||||
static DfgVertex* apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertex& vtx, uint32_t lsb,
|
||||
uint32_t width, bool aggressive) {
|
||||
TraceDriver traceDriver{dfg, vtx2Scc, vtx2Scc[vtx], aggressive};
|
||||
// Find the out-of-component driver of the given vertex
|
||||
DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb);
|
||||
DfgVertex* const resultp = traceDriver.trace(&vtx, lsb + width - 1, lsb);
|
||||
// Delete unused newly created vertices (these can be created if a
|
||||
// partial trace succeded, but an eventual one falied). Because new
|
||||
// vertices should be created depth first, it is enough to do a single
|
||||
|
|
@ -660,6 +665,7 @@ public:
|
|||
|
||||
class IndependentBits final : public DfgVisitor {
|
||||
// STATE
|
||||
const Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map
|
||||
const uint64_t m_component; // The component the start vertex is part of
|
||||
// Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'.
|
||||
std::unordered_map<const DfgVertex*, V3Number> m_vtxp2Mask;
|
||||
|
|
@ -675,9 +681,7 @@ class IndependentBits final : public DfgVisitor {
|
|||
std::forward_as_tuple(vtxp), //
|
||||
std::forward_as_tuple(vtxp->fileline(), static_cast<int>(vtxp->width()), 0));
|
||||
// Initialize to all ones if the vertex is part of the same component, otherwise zeroes
|
||||
if (pair.second && vtxp->getUser<uint64_t>() == m_component) {
|
||||
pair.first->second.setAllBits1();
|
||||
}
|
||||
if (pair.second && m_vtx2Scc.at(vtxp) == m_component) { pair.first->second.setAllBits1(); }
|
||||
return pair.first->second;
|
||||
}
|
||||
|
||||
|
|
@ -910,8 +914,9 @@ class IndependentBits final : public DfgVisitor {
|
|||
#undef MASK
|
||||
|
||||
// CONSTRUCTOR
|
||||
IndependentBits(DfgGraph& dfg, DfgVertex* vtxp)
|
||||
: m_component{vtxp->getUser<uint64_t>()} {
|
||||
IndependentBits(DfgGraph& dfg, const Vtx2Scc& vtx2Scc, DfgVertex& vertex)
|
||||
: m_vtx2Scc{vtx2Scc}
|
||||
, m_component{m_vtx2Scc.at(vertex)} {
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
|
|
@ -927,7 +932,7 @@ class IndependentBits final : public DfgVisitor {
|
|||
|
||||
// Enqueue every operation vertex in the analysed component
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
||||
if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx);
|
||||
}
|
||||
|
||||
// While there is an item on the worklist ...
|
||||
|
|
@ -940,7 +945,7 @@ class IndependentBits final : public DfgVisitor {
|
|||
// For an unpacked array vertex, just enque it's sinks.
|
||||
// (There can be no loops through arrays directly)
|
||||
currp->foreachSink([&](DfgVertex& vtx) {
|
||||
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
||||
if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx);
|
||||
return false;
|
||||
});
|
||||
continue;
|
||||
|
|
@ -957,7 +962,7 @@ class IndependentBits final : public DfgVisitor {
|
|||
// If mask changed, enqueue sinks
|
||||
if (!prevMask.isCaseEq(maskCurr)) {
|
||||
currp->foreachSink([&](DfgVertex& vtx) {
|
||||
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
||||
if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx);
|
||||
return false;
|
||||
});
|
||||
|
||||
|
|
@ -981,62 +986,78 @@ public:
|
|||
// independent of the vertex itself (logic is not circular). The result is
|
||||
// a conservative estimate, so bits reported dependent might not actually
|
||||
// be, but all bits reported independent are known to be so.
|
||||
static V3Number apply(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
IndependentBits independentBits{dfg, vtxp};
|
||||
static V3Number apply(DfgGraph& dfg, const Vtx2Scc& vtx2Scc, DfgVertex& vtx) {
|
||||
IndependentBits independentBits{dfg, vtx2Scc, vtx};
|
||||
// The mask represents the dependent bits, so invert it
|
||||
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
||||
result.opNot(independentBits.mask(vtxp));
|
||||
V3Number result{vtx.fileline(), static_cast<int>(vtx.width()), 0};
|
||||
result.opNot(independentBits.mask(&vtx));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class FixUpSelDrivers final {
|
||||
static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
size_t nImprovements = 0;
|
||||
const uint64_t component = vtxp->getUser<uint64_t>();
|
||||
vtxp->foreachSink([&](DfgVertex& sink) {
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map
|
||||
size_t m_nImprovements = 0; // Number of improvements mde
|
||||
|
||||
void fixUpSelSinks(DfgVertex& vtx) {
|
||||
const uint64_t component = m_vtx2Scc[vtx];
|
||||
vtx.foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (sink.getUser<uint64_t>() != component) return false;
|
||||
if (m_vtx2Scc[sink] != component) return false;
|
||||
// Only handle Sel
|
||||
DfgSel* const selp = sink.cast<DfgSel>();
|
||||
if (!selp) return false;
|
||||
// Try to find the driver of the selected bits outside the cycle
|
||||
DfgVertex* const fixp
|
||||
= TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false);
|
||||
= TraceDriver::apply(m_dfg, m_vtx2Scc, vtx, selp->lsb(), selp->width(), false);
|
||||
if (!fixp) return false;
|
||||
// Found an out-of-cycle driver. We can replace this sel with that.
|
||||
selp->replaceWith(fixp);
|
||||
selp->unlinkDelete(dfg);
|
||||
++nImprovements;
|
||||
selp->unlinkDelete(m_dfg);
|
||||
++m_nImprovements;
|
||||
return false;
|
||||
});
|
||||
return nImprovements;
|
||||
}
|
||||
|
||||
static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
size_t nImprovements = 0;
|
||||
const uint64_t component = vtxp->getUser<uint64_t>();
|
||||
vtxp->foreachSink([&](DfgVertex& sink) {
|
||||
void fixUpArraySelSinks(DfgVertex& vtx) {
|
||||
const uint64_t component = m_vtx2Scc[vtx];
|
||||
vtx.foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (sink.getUser<uint64_t>() != component) return false;
|
||||
if (m_vtx2Scc[sink] != component) return false;
|
||||
// Only handle ArraySels
|
||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||
if (!aselp) return false;
|
||||
DfgArraySel* const asp = sink.cast<DfgArraySel>();
|
||||
if (!asp) return false;
|
||||
// First, try to fix up the whole word
|
||||
if (DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false)) {
|
||||
DfgVertex* const fixp
|
||||
= TraceDriver::apply(m_dfg, m_vtx2Scc, *asp, 0, asp->width(), false);
|
||||
if (fixp) {
|
||||
// Found an out-of-cycle driver for the whole ArraySel
|
||||
aselp->replaceWith(fixp);
|
||||
aselp->unlinkDelete(dfg);
|
||||
++nImprovements;
|
||||
asp->replaceWith(fixp);
|
||||
asp->unlinkDelete(m_dfg);
|
||||
++m_nImprovements;
|
||||
return false;
|
||||
}
|
||||
// Attempt to fix up piece-wise at Sels applied to the ArraySel
|
||||
nImprovements += fixUpSelSinks(dfg, aselp);
|
||||
fixUpSelSinks(*asp);
|
||||
// Remove if became unused
|
||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
||||
if (!asp->hasSinks()) asp->unlinkDelete(m_dfg);
|
||||
return false;
|
||||
});
|
||||
return nImprovements;
|
||||
}
|
||||
|
||||
FixUpSelDrivers(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var)
|
||||
: m_dfg{dfg}
|
||||
, m_vtx2Scc{vtx2Scc} {
|
||||
UINFO(9, "FixUpSelDrivers of " << var.nodep()->name());
|
||||
if (var.isPacked()) {
|
||||
// For Packed variables, fix up all Sels applied to it
|
||||
fixUpSelSinks(var);
|
||||
} else if (var.isArray()) {
|
||||
// For Array variables, fix up each ArraySel applied to it
|
||||
fixUpArraySelSinks(var);
|
||||
}
|
||||
UINFO(9, "FixUpSelDrivers made " << m_nImprovements << " improvements");
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1044,26 +1065,20 @@ public:
|
|||
// expression of the selected bits. This can fix things like
|
||||
// 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common.
|
||||
// Returns the number of drivers fixed up.
|
||||
static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) {
|
||||
UINFO(9, "FixUpSelDrivers of " << varp->nodep()->name());
|
||||
size_t nImprovements = 0;
|
||||
if (varp->is<DfgVarPacked>()) {
|
||||
// For Packed variables, fix up all Sels applied to it
|
||||
nImprovements += fixUpSelSinks(dfg, varp);
|
||||
} else if (varp->is<DfgVarArray>()) {
|
||||
// For Array variables, fix up each ArraySel applied to it
|
||||
nImprovements += fixUpArraySelSinks(dfg, varp);
|
||||
}
|
||||
UINFO(9, "FixUpSelDrivers made " << nImprovements << " improvements");
|
||||
return nImprovements;
|
||||
static size_t apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) {
|
||||
return FixUpSelDrivers{dfg, vtx2Scc, var}.m_nImprovements;
|
||||
}
|
||||
};
|
||||
|
||||
class FixUpIndependentRanges final {
|
||||
// Returns a bitmask set if that bit of 'vtxp' is used (has a sink)
|
||||
static V3Number computeUsedBits(DfgVertex* vtxp) {
|
||||
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
||||
vtxp->foreachSink([&result](DfgVertex& sink) {
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map
|
||||
size_t m_nImprovements = 0; // Number of improvements mde
|
||||
|
||||
// Returns a bitmask set if that bit of 'vtx' is used (has a sink)
|
||||
static V3Number computeUsedBits(DfgVertex& vtx) {
|
||||
V3Number result{vtx.fileline(), static_cast<int>(vtx.width()), 0};
|
||||
vtx.foreachSink([&result](DfgVertex& sink) {
|
||||
// If used via a Sel, mark the selected bits used
|
||||
if (const DfgSel* const selp = sink.cast<DfgSel>()) {
|
||||
uint32_t lsb = selp->lsb();
|
||||
|
|
@ -1078,24 +1093,22 @@ class FixUpIndependentRanges final {
|
|||
return result;
|
||||
}
|
||||
|
||||
static std::string debugStr(const DfgVertex* vtxp) {
|
||||
if (const DfgArraySel* const aselp = vtxp->cast<DfgArraySel>()) {
|
||||
static std::string debugStr(const DfgVertex& vtx) {
|
||||
if (const DfgArraySel* const aselp = vtx.cast<DfgArraySel>()) {
|
||||
const size_t i = aselp->bitp()->as<DfgConst>()->toSizeT();
|
||||
return debugStr(aselp->fromp()) + "[" + std::to_string(i) + "]";
|
||||
return debugStr(*aselp->fromp()) + "[" + std::to_string(i) + "]";
|
||||
}
|
||||
if (const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>()) {
|
||||
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
||||
return varp->nodep()->name();
|
||||
}
|
||||
vtxp->v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE
|
||||
vtx.v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// Trace drivers of independent bits of 'vtxp' in the range '[hi:lo]'
|
||||
// append replacement terms to 'termps'. Returns number of successful
|
||||
// replacements.
|
||||
static size_t gatherTerms(std::vector<DfgVertex*>& termps, DfgGraph& dfg,
|
||||
DfgVertex* const vtxp, const V3Number& indpBits, const uint32_t hi,
|
||||
const uint32_t lo) {
|
||||
size_t nImprovements = 0;
|
||||
void gatherTerms(std::vector<DfgVertex*>& termps, DfgVertex& vtx, const V3Number& indpBits,
|
||||
const uint32_t hi, const uint32_t lo) {
|
||||
// Iterate through consecutive dependent/non-dependet ranges within [hi:lo]
|
||||
bool isIndependent = indpBits.bitIs1(lo);
|
||||
uint32_t lsb = lo;
|
||||
|
|
@ -1106,20 +1119,20 @@ class FixUpIndependentRanges final {
|
|||
DfgVertex* termp = nullptr;
|
||||
// If the range is not self-dependent, attempt to trace its driver
|
||||
if (isIndependent) {
|
||||
termp = TraceDriver::apply(dfg, vtxp, lsb, width, true);
|
||||
termp = TraceDriver::apply(m_dfg, m_vtx2Scc, vtx, lsb, width, true);
|
||||
if (termp) {
|
||||
++nImprovements;
|
||||
++m_nImprovements;
|
||||
} else {
|
||||
UINFO(5, "TraceDriver of independent range failed for "
|
||||
<< debugStr(vtxp) << "[" << msb << ":" << lsb << "]");
|
||||
<< debugStr(vtx) << "[" << msb << ":" << lsb << "]");
|
||||
}
|
||||
}
|
||||
// Fall back on using the part of the variable (if dependent, or trace failed)
|
||||
if (!termp) {
|
||||
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
||||
DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep};
|
||||
DfgSel* const selp = new DfgSel{m_dfg, vtx.fileline(), dtypep};
|
||||
// Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp'
|
||||
selp->setUser<uint64_t>(vtxp->getUser<uint64_t>());
|
||||
m_vtx2Scc[selp] = m_vtx2Scc.at(vtx);
|
||||
// Do not connect selp->fromp yet, need to do afer replacing 'vtxp'
|
||||
selp->lsb(lsb);
|
||||
termp = selp;
|
||||
|
|
@ -1130,40 +1143,38 @@ class FixUpIndependentRanges final {
|
|||
isIndependent = !isIndependent;
|
||||
lsb = msb + 1;
|
||||
}
|
||||
return nImprovements;
|
||||
}
|
||||
|
||||
static size_t fixUpPacked(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(VN_IS(vtxp->dtypep(), BasicDType), vtxp, "Should be a packed BasicDType");
|
||||
size_t nImprovements = 0;
|
||||
void fixUpPacked(DfgVertex& vtx) {
|
||||
UASSERT_OBJ(vtx.isPacked(), &vtx, "Should be a packed type");
|
||||
|
||||
// Figure out which bits of 'vtxp' are used
|
||||
const V3Number usedBits = computeUsedBits(vtxp);
|
||||
UINFO(9, "Used bits of '" << debugStr(vtxp) << "' are "
|
||||
<< usedBits.displayed(vtxp->fileline(), "%b"));
|
||||
const V3Number usedBits = computeUsedBits(vtx);
|
||||
UINFO(9, "Used bits of '" << debugStr(vtx) << "' are "
|
||||
<< usedBits.displayed(vtx.fileline(), "%b"));
|
||||
// Nothing to do if no bits are used
|
||||
if (usedBits.isEqZero()) return 0;
|
||||
if (usedBits.isEqZero()) return;
|
||||
|
||||
// Figure out which bits of 'vtxp' are dependent of themselves
|
||||
const V3Number indpBits = IndependentBits::apply(dfg, vtxp);
|
||||
UINFO(9, "Independent bits of '" << debugStr(vtxp) << "' are "
|
||||
<< indpBits.displayed(vtxp->fileline(), "%b"));
|
||||
const V3Number indpBits = IndependentBits::apply(m_dfg, m_vtx2Scc, vtx);
|
||||
UINFO(9, "Independent bits of '" << debugStr(vtx) << "' are "
|
||||
<< indpBits.displayed(vtx.fileline(), "%b"));
|
||||
// Can't do anything if all bits are dependent
|
||||
if (indpBits.isEqZero()) return 0;
|
||||
if (indpBits.isEqZero()) return;
|
||||
|
||||
{
|
||||
// Nothing to do if no used bits are independen (all used bits are dependent)
|
||||
V3Number usedAndIndependent{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
||||
V3Number usedAndIndependent{vtx.fileline(), static_cast<int>(vtx.width()), 0};
|
||||
usedAndIndependent.opAnd(usedBits, indpBits);
|
||||
if (usedAndIndependent.isEqZero()) return 0;
|
||||
if (usedAndIndependent.isEqZero()) return;
|
||||
}
|
||||
|
||||
// We are computing the terms to concatenate and replace 'vtxp' with
|
||||
std::vector<DfgVertex*> termps;
|
||||
|
||||
// Iterate through consecutive used/unused ranges
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
const uint32_t width = vtxp->width();
|
||||
FileLine* const flp = vtx.fileline();
|
||||
const uint32_t width = vtx.width();
|
||||
bool isUsed = usedBits.bitIs1(0); // Is current range used
|
||||
uint32_t lsb = 0; // LSB of current range
|
||||
for (uint32_t msb = 0; msb < width; ++msb) {
|
||||
|
|
@ -1171,11 +1182,11 @@ class FixUpIndependentRanges final {
|
|||
if (!endRange) continue;
|
||||
if (isUsed) {
|
||||
// The range is used, compute the replacement terms
|
||||
nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb);
|
||||
gatherTerms(termps, vtx, indpBits, msb, lsb);
|
||||
} else {
|
||||
// The range is not used, just use constant 0 as a placeholder
|
||||
DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1, 0};
|
||||
constp->setUser<uint64_t>(0);
|
||||
DfgConst* const constp = new DfgConst{m_dfg, flp, msb - lsb + 1, 0};
|
||||
m_vtx2Scc[constp] = 0;
|
||||
termps.emplace_back(constp);
|
||||
}
|
||||
// Next iteration
|
||||
|
|
@ -1184,21 +1195,21 @@ class FixUpIndependentRanges final {
|
|||
}
|
||||
|
||||
// If no imporovement was possible, delete the terms we just created
|
||||
if (!nImprovements) {
|
||||
for (DfgVertex* const termp : termps) VL_DO_DANGLING(termp->unlinkDelete(dfg), termp);
|
||||
if (!m_nImprovements) {
|
||||
for (DfgVertex* const tp : termps) VL_DO_DANGLING(tp->unlinkDelete(m_dfg), tp);
|
||||
termps.clear();
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// We managed to imporove something, apply the replacement
|
||||
// Concatenate all the terms to create the replacement
|
||||
DfgVertex* replacementp = termps.front();
|
||||
const uint64_t vComp = vtxp->getUser<uint64_t>();
|
||||
const uint64_t vComp = m_vtx2Scc.at(vtx);
|
||||
for (size_t i = 1; i < termps.size(); ++i) {
|
||||
DfgVertex* const termp = termps[i];
|
||||
const uint32_t catWidth = replacementp->width() + termp->width();
|
||||
AstNodeDType* const dtypep = V3Dfg::dtypePacked(catWidth);
|
||||
DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep};
|
||||
DfgConcat* const catp = new DfgConcat{m_dfg, flp, dtypep};
|
||||
catp->rhsp(replacementp);
|
||||
catp->lhsp(termp);
|
||||
// Need to figure out which component the replacement vertex
|
||||
|
|
@ -1206,56 +1217,61 @@ class FixUpIndependentRanges final {
|
|||
// component, it's part of that component, otherwise its not
|
||||
// cyclic (all terms are from outside the original component,
|
||||
// and feed into the original component).
|
||||
const uint64_t tComp = termp->getUser<uint64_t>();
|
||||
const uint64_t rComp = replacementp->getUser<uint64_t>();
|
||||
catp->setUser<uint64_t>(tComp == vComp || rComp == vComp ? vComp : 0);
|
||||
const uint64_t tComp = m_vtx2Scc.at(termp);
|
||||
const uint64_t rComp = m_vtx2Scc.at(replacementp);
|
||||
m_vtx2Scc[catp] = tComp == vComp || rComp == vComp ? vComp : 0;
|
||||
replacementp = catp;
|
||||
}
|
||||
|
||||
// Replace the vertex
|
||||
vtxp->replaceWith(replacementp);
|
||||
vtx.replaceWith(replacementp);
|
||||
// Connect the Sel nodes in the replacement
|
||||
for (DfgVertex* const termp : termps) {
|
||||
if (DfgSel* const selp = termp->cast<DfgSel>()) {
|
||||
if (!selp->fromp()) selp->fromp(vtxp);
|
||||
if (!selp->fromp()) selp->fromp(&vtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
return nImprovements;
|
||||
void main(DfgVertexVar& var) {
|
||||
UINFO(9, "FixUpIndependentRanges of " << var.nodep()->name());
|
||||
|
||||
if (var.is<DfgVarPacked>()) {
|
||||
// For Packed variables, fix up as whole
|
||||
fixUpPacked(var);
|
||||
} else if (var.is<DfgVarArray>()) {
|
||||
// For array variables, fix up element-wise
|
||||
const uint64_t component = m_vtx2Scc.at(var);
|
||||
var.foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (m_vtx2Scc.at(sink) != component) return false;
|
||||
// Only handle ArraySels with constant index
|
||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||
if (!aselp) return false;
|
||||
if (!aselp->bitp()->is<DfgConst>()) return false;
|
||||
// Fix up the word
|
||||
fixUpPacked(*aselp);
|
||||
// Remove if became unused
|
||||
if (!aselp->hasSinks()) aselp->unlinkDelete(m_dfg);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
UINFO(9, "FixUpIndependentRanges made " << m_nImprovements << " improvements");
|
||||
}
|
||||
|
||||
FixUpIndependentRanges(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var)
|
||||
: m_dfg{dfg}
|
||||
, m_vtx2Scc{vtx2Scc} {
|
||||
main(var);
|
||||
}
|
||||
|
||||
public:
|
||||
// Similar to FixUpSelDrivers, but first comptute which bits of the
|
||||
// variable are self dependent, and fix up those that are independent
|
||||
// but used.
|
||||
static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) {
|
||||
UINFO(9, "FixUpIndependentRanges of " << varp->nodep()->name());
|
||||
size_t nImprovements = 0;
|
||||
|
||||
if (varp->is<DfgVarPacked>()) {
|
||||
// For Packed variables, fix up as whole
|
||||
nImprovements += fixUpPacked(dfg, varp);
|
||||
} else if (varp->is<DfgVarArray>()) {
|
||||
// For array variables, fix up element-wise
|
||||
const uint64_t component = varp->getUser<uint64_t>();
|
||||
varp->foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (sink.getUser<uint64_t>() != component) return false;
|
||||
// Only handle ArraySels with constant index
|
||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||
if (!aselp) return false;
|
||||
if (!aselp->bitp()->is<DfgConst>()) return false;
|
||||
// Fix up the word
|
||||
nImprovements += fixUpPacked(dfg, aselp);
|
||||
// Remove if became unused
|
||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
UINFO(9, "FixUpIndependentRanges made " << nImprovements << " improvements");
|
||||
return nImprovements;
|
||||
static size_t apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) {
|
||||
return FixUpIndependentRanges{dfg, vtx2Scc, var}.m_nImprovements;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1292,8 +1308,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
|
|||
// Iterate while an improvement can be made and the graph is still cyclic
|
||||
do {
|
||||
// Color SCCs (populates DfgVertex::user<uint64_t>())
|
||||
const auto userDataInUse = res.userDataInUse();
|
||||
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(res);
|
||||
Vtx2Scc vtx2Scc = res.makeUserMap<uint64_t>();
|
||||
const uint32_t numNonTrivialSCCs
|
||||
= V3DfgPasses::colorStronglyConnectedComponents(res, vtx2Scc);
|
||||
|
||||
// Congrats if it has become acyclic
|
||||
if (!numNonTrivialSCCs) {
|
||||
|
|
@ -1310,10 +1327,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
|
|||
// Method 1: FixUpSelDrivers
|
||||
for (DfgVertexVar& vtx : res.varVertices()) {
|
||||
// If Variable is not part of a cycle, move on
|
||||
const uint64_t component = vtx.getUser<uint64_t>();
|
||||
if (!component) continue;
|
||||
if (!vtx2Scc[vtx]) continue;
|
||||
|
||||
const size_t nFixed = FixUpSelDrivers::apply(res, &vtx);
|
||||
const size_t nFixed = FixUpSelDrivers::apply(res, vtx2Scc, vtx);
|
||||
if (nFixed) {
|
||||
nImprovements += nFixed;
|
||||
ctx.m_breakCyclesContext.m_nImprovements += nFixed;
|
||||
|
|
@ -1327,10 +1343,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
|
|||
// Method 2. FixUpIndependentRanges
|
||||
for (DfgVertexVar& vtx : res.varVertices()) {
|
||||
// If Variable is not part of a cycle, move on
|
||||
const uint64_t component = vtx.getUser<uint64_t>();
|
||||
if (!component) continue;
|
||||
if (!vtx2Scc[vtx]) continue;
|
||||
|
||||
const size_t nFixed = FixUpIndependentRanges::apply(res, &vtx);
|
||||
const size_t nFixed = FixUpIndependentRanges::apply(res, vtx2Scc, vtx);
|
||||
if (nFixed) {
|
||||
nImprovements += nFixed;
|
||||
ctx.m_breakCyclesContext.m_nImprovements += nFixed;
|
||||
|
|
@ -1340,10 +1355,11 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
|
|||
} while (nImprovements != prevNImprovements);
|
||||
|
||||
if (dumpDfgLevel() >= 9) {
|
||||
const auto userDataInUse = res.userDataInUse();
|
||||
V3DfgPasses::colorStronglyConnectedComponents(res);
|
||||
res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining",
|
||||
[](const DfgVertex& vtx) { return vtx.getUser<uint64_t>(); });
|
||||
Vtx2Scc vtx2Scc = res.makeUserMap<uint64_t>();
|
||||
V3DfgPasses::colorStronglyConnectedComponents(res, vtx2Scc);
|
||||
res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining", [&](const DfgVertex& vtx) {
|
||||
return vtx2Scc[vtx]; //
|
||||
});
|
||||
}
|
||||
|
||||
// If an improvement was made, return the still cyclic improved graph
|
||||
|
|
|
|||
|
|
@ -26,52 +26,51 @@
|
|||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
// Similar algorithm used in ExtractCyclicComponents.
|
||||
// This one sets DfgVertex::user(). See the static 'apply' method below.
|
||||
class ColorStronglyConnectedComponents final {
|
||||
static_assert(sizeof(uint32_t[2]) == sizeof(uint64_t), "Incorrect ovverlay size");
|
||||
|
||||
// CONSTANTS
|
||||
static constexpr uint32_t UNASSIGNED = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
// TYPES
|
||||
struct VertexState final {
|
||||
uint32_t component = UNASSIGNED; // Result component number (0 means not in SCC)
|
||||
uint32_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs
|
||||
VertexState() = default;
|
||||
VertexState(uint32_t i, uint32_t n)
|
||||
: component{n}
|
||||
, index{i} {}
|
||||
};
|
||||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The input graph
|
||||
const DfgGraph& m_dfg; // The input graph
|
||||
DfgUserMap<uint64_t>& m_map; // The result map we are computing - also used for traversal
|
||||
uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph
|
||||
uint32_t m_index = 0; // Visitation index counter
|
||||
std::vector<DfgVertex*> m_stack; // The stack used by the algorithm
|
||||
std::vector<const DfgVertex*> m_stack; // The stack used by the algorithm
|
||||
|
||||
// METHODS
|
||||
void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) {
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex"););
|
||||
// Use the bottom 32-bit word as the component number
|
||||
uint32_t& component(const DfgVertex& vtx) { //
|
||||
return reinterpret_cast<uint32_t(&)[2]>(m_map[vtx])[0];
|
||||
}
|
||||
// Use the top 32-bit word as the visitation index
|
||||
uint32_t& index(const DfgVertex& vtx) { //
|
||||
return reinterpret_cast<uint32_t(&)[2]>(m_map[vtx])[1];
|
||||
}
|
||||
|
||||
void visitColorSCCs(const DfgVertex& vtx) {
|
||||
UDEBUGONLY(UASSERT_OBJ(index(vtx) == UNASSIGNED, &vtx, "Already visited vertex"););
|
||||
|
||||
// Visiting vertex
|
||||
const size_t rootIndex = vtxState.index = ++m_index;
|
||||
const size_t rootIndex = index(vtx) = ++m_index;
|
||||
|
||||
// Visit children
|
||||
vtx.foreachSink([&](DfgVertex& child) {
|
||||
VertexState& childSatate = child.user<VertexState>();
|
||||
vtx.foreachSink([&](const DfgVertex& child) {
|
||||
// If the child has not yet been visited, then continue traversal
|
||||
if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate);
|
||||
if (index(child) == UNASSIGNED) visitColorSCCs(child);
|
||||
// If the child is not in an SCC
|
||||
if (childSatate.component == UNASSIGNED) {
|
||||
if (vtxState.index > childSatate.index) vtxState.index = childSatate.index;
|
||||
if (component(child) == UNASSIGNED) {
|
||||
if (index(vtx) > index(child)) index(vtx) = index(child);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (vtxState.index == rootIndex) {
|
||||
if (index(vtx) == rootIndex) {
|
||||
// This is the 'root' of an SCC
|
||||
|
||||
// A trivial SCC contains only a single vertex
|
||||
const bool isTrivial = m_stack.empty() //
|
||||
|| m_stack.back()->getUser<VertexState>().index < rootIndex;
|
||||
const bool isTrivial = m_stack.empty() || index(*m_stack.back()) < rootIndex;
|
||||
// We also need a separate component for vertices that drive themselves (which can
|
||||
// happen for input like 'assign a = a'), as we want to extract them (they are cyclic).
|
||||
const bool drivesSelf = vtx.foreachSink([&](const DfgVertex& sink) { //
|
||||
|
|
@ -81,17 +80,16 @@ class ColorStronglyConnectedComponents final {
|
|||
if (!isTrivial || drivesSelf) {
|
||||
// Allocate new component
|
||||
++m_nonTrivialSCCs;
|
||||
vtxState.component = m_nonTrivialSCCs;
|
||||
component(vtx) = m_nonTrivialSCCs;
|
||||
while (!m_stack.empty()) {
|
||||
VertexState& topState = m_stack.back()->getUser<VertexState>();
|
||||
// Only higher nodes belong to the same SCC
|
||||
if (topState.index < rootIndex) break;
|
||||
if (index(*m_stack.back()) < rootIndex) break;
|
||||
component(*m_stack.back()) = m_nonTrivialSCCs;
|
||||
m_stack.pop_back();
|
||||
topState.component = m_nonTrivialSCCs;
|
||||
}
|
||||
} else {
|
||||
// Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph.
|
||||
vtxState.component = 0;
|
||||
component(vtx) = 0;
|
||||
}
|
||||
} else {
|
||||
// Not the root of an SCC
|
||||
|
|
@ -102,56 +100,61 @@ class ColorStronglyConnectedComponents final {
|
|||
void colorSCCs() {
|
||||
// We know constant nodes have no input edges, so they cannot be part
|
||||
// of a non-trivial SCC. Mark them as such without any real traversals.
|
||||
for (DfgConst& vtx : m_dfg.constVertices()) vtx.setUser(VertexState{0, 0});
|
||||
|
||||
// Start traversals through variables
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
VertexState& vtxState = vtx.user<VertexState>();
|
||||
// If it has no input or no outputs, it cannot be part of a non-trivial SCC.
|
||||
if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) {
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
|
||||
&vtx, "Non circular variable must be in a trivial SCC"););
|
||||
vtxState.index = 0;
|
||||
vtxState.component = 0;
|
||||
continue;
|
||||
}
|
||||
// If not yet visited, start a traversal
|
||||
if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState);
|
||||
for (const DfgConst& vtx : m_dfg.constVertices()) {
|
||||
index(vtx) = 0;
|
||||
component(vtx) = 0;
|
||||
}
|
||||
|
||||
// Start traversals through operations
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
VertexState& vtxState = vtx.user<VertexState>();
|
||||
// If not yet visited, start a traversal
|
||||
if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState);
|
||||
// Initialize state of variable vertices
|
||||
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// If it has no inputs or no outputs, it cannot be part of a non-trivial SCC.
|
||||
if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) {
|
||||
index(vtx) = 0;
|
||||
component(vtx) = 0;
|
||||
continue;
|
||||
}
|
||||
index(vtx) = UNASSIGNED;
|
||||
component(vtx) = UNASSIGNED;
|
||||
}
|
||||
|
||||
// Initialize state of operation vertices
|
||||
for (const DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
index(vtx) = UNASSIGNED;
|
||||
component(vtx) = UNASSIGNED;
|
||||
}
|
||||
|
||||
// Start traversals through not yet visited variables
|
||||
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
if (index(vtx) == UNASSIGNED) visitColorSCCs(vtx);
|
||||
}
|
||||
|
||||
// Start traversals through not yet visited operations
|
||||
for (const DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
if (index(vtx) == UNASSIGNED) visitColorSCCs(vtx);
|
||||
}
|
||||
}
|
||||
|
||||
explicit ColorStronglyConnectedComponents(DfgGraph& dfg)
|
||||
: m_dfg{dfg} {
|
||||
explicit ColorStronglyConnectedComponents(const DfgGraph& dfg, DfgUserMap<uint64_t>& map)
|
||||
: m_dfg{dfg}
|
||||
, m_map{map} {
|
||||
UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name());
|
||||
// Yet another implementation of Pearce's algorithm.
|
||||
colorSCCs();
|
||||
// Re-assign user values
|
||||
m_dfg.forEachVertex([](DfgVertex& vtx) {
|
||||
const uint64_t component = vtx.getUser<VertexState>().component;
|
||||
vtx.setUser<uint64_t>(component);
|
||||
// Re-assign mapped values
|
||||
m_dfg.forEachVertex([&](const DfgVertex& vtx) {
|
||||
const uint64_t c = component(vtx);
|
||||
map[vtx] = c;
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
// Sets DfgVertex::user<uint64_t>() for all vertext to:
|
||||
// - 0, if the vertex is not part of a non-trivial strongly connected component
|
||||
// and is not part of a self-loop. That is: the Vertex is not part of any cycle.
|
||||
// - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N.
|
||||
// That is: each set of vertices that are reachable from each other will have the same
|
||||
// non-zero value assigned.
|
||||
// Returns the number of non-trivial SCCs (~distinct cycles)
|
||||
static uint32_t apply(DfgGraph& dfg) {
|
||||
return ColorStronglyConnectedComponents{dfg}.m_nonTrivialSCCs;
|
||||
// See declaration of V3DfgPasses::colorStronglyConnectedComponents
|
||||
static uint32_t apply(const DfgGraph& dfg, DfgUserMap<uint64_t>& map) {
|
||||
return ColorStronglyConnectedComponents{dfg, map}.m_nonTrivialSCCs;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t V3DfgPasses::colorStronglyConnectedComponents(DfgGraph& dfg) {
|
||||
return ColorStronglyConnectedComponents::apply(dfg);
|
||||
uint32_t V3DfgPasses::colorStronglyConnectedComponents(const DfgGraph& dfg,
|
||||
DfgUserMap<uint64_t>& map) {
|
||||
return ColorStronglyConnectedComponents::apply(dfg, map);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ class SplitIntoComponents final {
|
|||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The input graph
|
||||
// Map from vertices to the weekly connected component they belong to
|
||||
DfgUserMap<size_t> m_component = m_dfg.makeUserMap<size_t>();
|
||||
const std::string m_prefix; // Component name prefix
|
||||
std::vector<std::unique_ptr<DfgGraph>> m_components; // The extracted components
|
||||
// Component counter - starting from 1 as 0 is the default value used as a marker
|
||||
|
|
@ -44,10 +46,10 @@ class SplitIntoComponents final {
|
|||
std::vector<DfgVertex*> queue;
|
||||
queue.reserve(m_dfg.size());
|
||||
|
||||
// any sort of interesting logic must involve a variable, so we only need to iterate them
|
||||
// Any sort of interesting logic must involve a variable, so we only need to iterate them
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// If already assigned this vertex to a component, then continue
|
||||
if (vtx.user<size_t>()) continue;
|
||||
if (m_component[vtx]) continue;
|
||||
|
||||
// Start depth first traversal at this vertex
|
||||
queue.push_back(&vtx);
|
||||
|
|
@ -59,10 +61,10 @@ class SplitIntoComponents final {
|
|||
queue.pop_back();
|
||||
|
||||
// Move on if already visited
|
||||
if (item.user<size_t>()) continue;
|
||||
if (m_component[item]) continue;
|
||||
|
||||
// Assign to current component
|
||||
item.user<size_t>() = m_componentCounter;
|
||||
m_component[item] = m_componentCounter;
|
||||
|
||||
// Enqueue all sources and sinks of this vertex.
|
||||
item.foreachSource([&](DfgVertex& src) {
|
||||
|
|
@ -83,7 +85,7 @@ class SplitIntoComponents final {
|
|||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
if (const size_t component = vtxp->user<size_t>()) {
|
||||
if (const size_t component = m_component[vtxp]) {
|
||||
m_dfg.removeVertex(*vtxp);
|
||||
m_components[component - 1]->addVertex(*vtxp);
|
||||
} else {
|
||||
|
|
@ -96,8 +98,6 @@ class SplitIntoComponents final {
|
|||
SplitIntoComponents(DfgGraph& dfg, const std::string& label)
|
||||
: m_dfg{dfg}
|
||||
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
||||
// Component number is stored as DfgVertex::user<size_t>()
|
||||
const auto userDataInUse = m_dfg.userDataInUse();
|
||||
// Color each component of the graph
|
||||
colorComponents();
|
||||
// Allocate the component graphs
|
||||
|
|
@ -126,6 +126,8 @@ std::vector<std::unique_ptr<DfgGraph>> DfgGraph::splitIntoComponents(const std::
|
|||
class ExtractCyclicComponents final {
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The input graph
|
||||
// Map from vertices to the component they belong to
|
||||
DfgUserMap<uint64_t> m_component = m_dfg.makeUserMap<uint64_t>();
|
||||
const std::string m_prefix; // Component name prefix
|
||||
const bool m_doExpensiveChecks = v3Global.opt.debugCheck();
|
||||
// The extracted cyclic components
|
||||
|
|
@ -137,19 +139,21 @@ class ExtractCyclicComponents final {
|
|||
void addVertexAndExpandSiblings(DfgVertex& vtx, uint64_t component) {
|
||||
// Do not go past a variable, we will partition the graph there
|
||||
if (vtx.is<DfgVertexVar>()) return;
|
||||
// Pick up component value reference
|
||||
uint64_t& vtxComponentr = m_component.at(vtx);
|
||||
// Don't need to recurse if the vertex is already in the same component,
|
||||
// it was either marked through an earlier traversal, in which case it
|
||||
// was processed recursively, or it will be processed later.
|
||||
if (vtx.getUser<uint64_t>() == component) return;
|
||||
if (vtxComponentr == component) return;
|
||||
// Because all cycles are through a variable, we can't reach another SCC.
|
||||
UASSERT_OBJ(!vtx.getUser<uint64_t>(), &vtx, "Cycle without variable involvement");
|
||||
UASSERT_OBJ(!vtxComponentr, &vtx, "Cycle without variable involvement");
|
||||
// Put this vertex in the component, and continue recursively
|
||||
vtx.setUser<uint64_t>(component);
|
||||
vtxComponentr = component;
|
||||
expandSiblings(vtx, component);
|
||||
}
|
||||
|
||||
void expandSiblings(DfgVertex& vtx, uint64_t component) {
|
||||
UASSERT_OBJ(vtx.getUser<uint64_t>() == component, &vtx, "Traversal didn't stop");
|
||||
UASSERT_OBJ(m_component.at(vtx) == component, &vtx, "Traversal didn't stop");
|
||||
vtx.foreachSink([&](DfgVertex& v) {
|
||||
addVertexAndExpandSiblings(v, component);
|
||||
return false;
|
||||
|
|
@ -179,22 +183,22 @@ class ExtractCyclicComponents final {
|
|||
// component if they are not variables themselves. The assertions below
|
||||
// must hold because of the assumption above.
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
const uint64_t varComponent = vtx.getUser<uint64_t>();
|
||||
const uint64_t varComponent = m_component.at(vtx);
|
||||
if (!varComponent) continue;
|
||||
if (DfgVertex* const srcp = vtx.srcp()) {
|
||||
if (!srcp->is<DfgVertexVar>()) {
|
||||
const uint64_t srcComponent = srcp->getUser<uint64_t>();
|
||||
uint64_t& srcComponent = m_component.at(srcp);
|
||||
UASSERT_OBJ(!srcComponent || srcComponent == varComponent, srcp,
|
||||
"Cycle through 'srcp' that does not go through variable.");
|
||||
srcp->setUser<uint64_t>(varComponent);
|
||||
srcComponent = varComponent;
|
||||
}
|
||||
}
|
||||
if (DfgVertex* const defp = vtx.defaultp()) {
|
||||
if (!defp->is<DfgVertexVar>()) {
|
||||
const uint64_t defComponent = defp->getUser<uint64_t>();
|
||||
uint64_t& defComponent = m_component.at(defp);
|
||||
UASSERT_OBJ(!defComponent || defComponent == varComponent, defp,
|
||||
"Cycle through 'defaultp' that does not go through variable");
|
||||
defp->setUser<uint64_t>(varComponent);
|
||||
defComponent = varComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +209,7 @@ class ExtractCyclicComponents final {
|
|||
// We do this by staring a DFS from each vertex that is part of an
|
||||
// component and add all reachable non-variable vertices to the same.
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
if (const uint64_t targetComponent = vtx.getUser<uint64_t>()) {
|
||||
if (const uint64_t targetComponent = m_component.at(vtx)) {
|
||||
expandSiblings(vtx, targetComponent);
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +217,7 @@ class ExtractCyclicComponents final {
|
|||
|
||||
// Retrieve clone of vertex in the given component
|
||||
DfgVertexVar* getClone(DfgVertexVar& vtx, uint64_t component) {
|
||||
UASSERT_OBJ(vtx.getUser<uint64_t>() != component, &vtx, "Vertex is in that component");
|
||||
UASSERT_OBJ(m_component.at(vtx) != component, &vtx, "Vertex is in that component");
|
||||
DfgVertexVar*& clonep = m_clones[&vtx][component];
|
||||
if (!clonep) {
|
||||
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
|
|
@ -230,7 +234,7 @@ class ExtractCyclicComponents final {
|
|||
}
|
||||
}
|
||||
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type");
|
||||
clonep->setUser<uint64_t>(component);
|
||||
m_component[clonep] = component;
|
||||
clonep->tmpForp(vtx.tmpForp());
|
||||
}
|
||||
return clonep;
|
||||
|
|
@ -238,11 +242,11 @@ class ExtractCyclicComponents final {
|
|||
|
||||
// Fix edges that cross components
|
||||
void fixEdges(DfgVertexVar& vtx) {
|
||||
const uint64_t component = vtx.getUser<uint64_t>();
|
||||
const uint64_t component = m_component.at(vtx);
|
||||
|
||||
// Fix up srcp and dstp (they must be the same component, or variable)
|
||||
if (DfgVertex* const sp = vtx.srcp()) {
|
||||
const uint64_t srcComponent = sp->getUser<uint64_t>();
|
||||
const uint64_t srcComponent = m_component.at(sp);
|
||||
if (srcComponent != component) {
|
||||
UASSERT_OBJ(sp->is<DfgVertexVar>(), &vtx, "'srcp' in different component");
|
||||
getClone(vtx, srcComponent)->srcp(sp);
|
||||
|
|
@ -250,7 +254,7 @@ class ExtractCyclicComponents final {
|
|||
}
|
||||
}
|
||||
if (DfgVertex* const dp = vtx.defaultp()) {
|
||||
const uint64_t defaultComponent = dp->getUser<uint64_t>();
|
||||
const uint64_t defaultComponent = m_component.at(dp);
|
||||
if (defaultComponent != component) {
|
||||
UASSERT_OBJ(dp->is<DfgVertexVar>(), &vtx, "'defaultp' in different component");
|
||||
getClone(vtx, defaultComponent)->defaultp(dp);
|
||||
|
|
@ -264,7 +268,7 @@ class ExtractCyclicComponents final {
|
|||
return false;
|
||||
});
|
||||
for (DfgVertex* const sinkp : sinkps) {
|
||||
const uint64_t sinkComponent = sinkp->getUser<uint64_t>();
|
||||
const uint64_t sinkComponent = m_component.at(sinkp);
|
||||
// Same component is OK
|
||||
if (sinkComponent == component) continue;
|
||||
DfgVertex* const clonep = getClone(vtx, sinkComponent);
|
||||
|
|
@ -278,7 +282,7 @@ class ExtractCyclicComponents final {
|
|||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
DfgVertex& vtx = *vtxp;
|
||||
if (const uint64_t component = vtx.getUser<uint64_t>()) {
|
||||
if (const uint64_t component = m_component.at(vtx)) {
|
||||
m_dfg.removeVertex(vtx);
|
||||
m_components[component - 1]->addVertex(vtx);
|
||||
}
|
||||
|
|
@ -289,10 +293,10 @@ class ExtractCyclicComponents final {
|
|||
// Check that edges only cross components at variable boundaries
|
||||
dfg.forEachVertex([&](DfgVertex& vtx) {
|
||||
if (vtx.is<DfgVarPacked>()) return;
|
||||
const uint64_t component = vtx.getUser<uint64_t>();
|
||||
const uint64_t component = m_component.at(vtx);
|
||||
vtx.foreachSink([&](DfgVertex& snk) {
|
||||
if (snk.is<DfgVertexVar>()) return false; // OK to cross at variables
|
||||
UASSERT_OBJ(component == snk.getUser<uint64_t>(), &vtx,
|
||||
UASSERT_OBJ(component == m_component.at(snk), &vtx,
|
||||
"Edge crossing components without variable involvement");
|
||||
return false;
|
||||
});
|
||||
|
|
@ -317,10 +321,10 @@ class ExtractCyclicComponents final {
|
|||
});
|
||||
}
|
||||
|
||||
void extractComponents(uint32_t numNonTrivialSCCs) {
|
||||
void extractComponents(uint32_t nComponents) {
|
||||
// Allocate result graphs
|
||||
m_components.resize(numNonTrivialSCCs);
|
||||
for (uint32_t i = 0; i < numNonTrivialSCCs; ++i) {
|
||||
m_components.resize(nComponents);
|
||||
for (uint32_t i = 0; i < nComponents; ++i) {
|
||||
m_components[i].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i)});
|
||||
}
|
||||
|
||||
|
|
@ -359,16 +363,14 @@ class ExtractCyclicComponents final {
|
|||
explicit ExtractCyclicComponents(DfgGraph& dfg, const std::string& label)
|
||||
: m_dfg{dfg}
|
||||
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
||||
// DfgVertex::user<uint64_t> is set to the SCC number by colorStronglyConnectedComponents,
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
// Find all the non-trivial SCCs (and trivial cycles) in the graph
|
||||
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg);
|
||||
const uint32_t nSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg, m_component);
|
||||
// If the graph was acyclic (which should be the common case), then we are done.
|
||||
if (!numNonTrivialSCCs) return;
|
||||
if (!nSCCs) return;
|
||||
// Ensure that component boundaries are always at variables, by expanding SCCs
|
||||
expandComponents();
|
||||
// Extract the components
|
||||
extractComponents(numNonTrivialSCCs);
|
||||
extractComponents(nSCCs);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
// Remove common sub-expressions
|
||||
{
|
||||
// Used by DfgVertex::hash
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
DfgUserMap<V3Hash> hashCache = dfg.makeUserMap<V3Hash>();
|
||||
|
||||
DfgVertex::EqualsCache equalsCache;
|
||||
std::unordered_map<V3Hash, std::vector<DfgVertex*>> verticesWithEqualHashes;
|
||||
|
|
@ -38,7 +38,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
|
||||
// Pre-hash variables, these are all unique, so just set their hash to a unique value
|
||||
uint32_t varHash = 0;
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) vtx.user<V3Hash>() = V3Hash{++varHash};
|
||||
for (const DfgVertexVar& vtx : dfg.varVertices()) hashCache[vtx] = V3Hash{++varHash};
|
||||
|
||||
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
|
||||
// expressions using the same constants to be combined, so we do need to hash equal
|
||||
|
|
@ -49,7 +49,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
continue;
|
||||
}
|
||||
vtxp->user<V3Hash>() = vtxp->num().toHash() + varHash;
|
||||
hashCache[vtxp] = vtxp->num().toHash() + varHash;
|
||||
}
|
||||
|
||||
// Combine operation vertices
|
||||
|
|
@ -59,7 +59,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) {
|
|||
vtxp->unlinkDelete(dfg);
|
||||
continue;
|
||||
}
|
||||
const V3Hash hash = vtxp->hash();
|
||||
const V3Hash hash = vtxp->hash(hashCache);
|
||||
std::vector<DfgVertex*>& vec = verticesWithEqualHashes[hash];
|
||||
bool replaced = false;
|
||||
for (DfgVertex* const candidatep : vec) {
|
||||
|
|
@ -91,8 +91,8 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
|||
}
|
||||
|
||||
void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
// DfgVertex::user is the next pointer of the work list elements
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
// Map from vertex to next vertex in the work list
|
||||
DfgUserMap<DfgVertex*> nextp = dfg.makeUserMap<DfgVertex*>();
|
||||
|
||||
// Head of work list. Note that we want all next pointers in the list to be non-zero (including
|
||||
// that of the last element). This allows as to do two important things: detect if an element
|
||||
|
|
@ -102,29 +102,22 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
DfgVertex* const sentinelp = reinterpret_cast<DfgVertex*>(&dfg);
|
||||
DfgVertex* workListp = sentinelp;
|
||||
|
||||
// Add all unused operation vertices to the work list. This also allocates all DfgVertex::user.
|
||||
// Add all unused operation vertices to the work list
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.hasSinks()) {
|
||||
// This vertex is used. Allocate user, but don't add to work list.
|
||||
vtx.setUser<DfgVertex*>(nullptr);
|
||||
} else {
|
||||
// This vertex is unused. Add to work list.
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
if (vtx.hasSinks()) continue;
|
||||
// This vertex is unused. Add to work list.
|
||||
nextp[vtx] = workListp;
|
||||
workListp = &vtx;
|
||||
}
|
||||
|
||||
// Also add all unused temporaries created during synthesis
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
if (!vtx.tmpForp()) continue;
|
||||
if (vtx.hasSinks() || vtx.hasDfgRefs()) {
|
||||
// This vertex is used. Allocate user, but don't add to work list.
|
||||
vtx.setUser<DfgVertex*>(nullptr);
|
||||
} else {
|
||||
// This vertex is unused. Add to work list.
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
if (vtx.hasSinks()) continue;
|
||||
if (vtx.hasDfgRefs()) continue;
|
||||
// This vertex is unused. Add to work list.
|
||||
nextp[vtx] = workListp;
|
||||
workListp = &vtx;
|
||||
}
|
||||
|
||||
// Process the work list
|
||||
|
|
@ -132,11 +125,11 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
// Pick up the head
|
||||
DfgVertex* const vtxp = workListp;
|
||||
// Detach the head
|
||||
workListp = vtxp->getUser<DfgVertex*>();
|
||||
workListp = nextp.at(vtxp);
|
||||
// Prefetch next item
|
||||
VL_PREFETCH_RW(workListp);
|
||||
// This item is now off the work list
|
||||
vtxp->setUser<DfgVertex*>(nullptr);
|
||||
nextp.at(vtxp) = nullptr;
|
||||
// DfgLogic should have been synthesized or removed
|
||||
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "Should not be DfgLogic");
|
||||
// If used, then nothing to do, so move on
|
||||
|
|
@ -153,9 +146,9 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
const DfgVertexVar* const varp = src.cast<DfgVertexVar>();
|
||||
if (varp && !varp->tmpForp()) return false;
|
||||
// If already in work list then nothing to do
|
||||
if (src.getUser<DfgVertex*>()) return false;
|
||||
if (nextp[src]) return false;
|
||||
// Actually add to work list.
|
||||
src.setUser<DfgVertex*>(workListp);
|
||||
nextp[src] = workListp;
|
||||
workListp = &src;
|
||||
return false;
|
||||
});
|
||||
|
|
@ -177,8 +170,6 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
UASSERT(dfg.modulep(), "binToOneHot only works with unscoped DfgGraphs for now");
|
||||
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
|
||||
// Structure to keep track of comparison details
|
||||
struct Term final {
|
||||
DfgVertex* m_vtxp = nullptr; // Vertex to replace
|
||||
|
|
@ -189,13 +180,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
, m_inv{inv} {}
|
||||
};
|
||||
|
||||
// Map from 'value beign compared' -> 'terms', stored in DfgVertex::user()
|
||||
using Val2Terms = std::map<uint32_t, std::vector<Term>>;
|
||||
// Allocator for Val2Terms, so it's cleaned up on return
|
||||
std::deque<Val2Terms> val2TermsAllocator;
|
||||
// List of vertices that are used as sources
|
||||
std::vector<DfgVertex*> srcps;
|
||||
|
||||
// Map from 'vertices' -> 'value beign compared' -> 'terms'
|
||||
using Val2Terms = std::map<uint32_t, std::vector<Term>>;
|
||||
DfgUserMap<Val2Terms> vtx2Val2Terms = dfg.makeUserMap<Val2Terms>();
|
||||
|
||||
// Only consider input variables from a reasonable range:
|
||||
// - not too big to avoid huge tables, you are doomed anyway at that point..
|
||||
// - not too small, as it's probably not worth it
|
||||
|
|
@ -255,16 +246,12 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
// Not a comparison-like vertex
|
||||
continue;
|
||||
}
|
||||
// Grab the Val2Terms entry
|
||||
Val2Terms*& val2Termspr = srcp->user<Val2Terms*>();
|
||||
if (!val2Termspr) {
|
||||
// Remeber and allocate on first encounter
|
||||
srcps.emplace_back(srcp);
|
||||
val2TermsAllocator.emplace_back();
|
||||
val2Termspr = &val2TermsAllocator.back();
|
||||
}
|
||||
// Grab Val2Terms for this vertex
|
||||
Val2Terms& val2Terms = vtx2Val2Terms[srcp];
|
||||
// Remeber and on first encounter
|
||||
if (val2Terms.empty()) srcps.emplace_back(srcp);
|
||||
// Record term
|
||||
(*val2Termspr)[val].emplace_back(&vtx, inv);
|
||||
val2Terms[val].emplace_back(&vtx, inv);
|
||||
++nTerms;
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +268,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
|
||||
// Create decoders for each srcp
|
||||
for (DfgVertex* const srcp : srcps) {
|
||||
const Val2Terms& val2Terms = *srcp->getUser<Val2Terms*>();
|
||||
const Val2Terms& val2Terms = vtx2Val2Terms[srcp];
|
||||
|
||||
// If not enough terms in this vertex, ignore
|
||||
if (val2Terms.size() < TERM_LIMIT) continue;
|
||||
|
|
@ -422,7 +409,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
}
|
||||
|
||||
void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
// Map from vertex to next vertex in the work list
|
||||
DfgUserMap<DfgVertex*> nextp = dfg.makeUserMap<DfgVertex*>();
|
||||
|
||||
// Head of work list. Note that we want all next pointers in the list to be non-zero
|
||||
// (including that of the last element). This allows us to do two important things: detect
|
||||
|
|
@ -434,16 +422,16 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
|
||||
// Add all variables to the initial work list
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
nextp[vtx] = workListp;
|
||||
workListp = &vtx;
|
||||
}
|
||||
|
||||
const auto addToWorkList = [&](DfgVertex& vtx) {
|
||||
// If already in work list then nothing to do
|
||||
DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>();
|
||||
if (nextInWorklistp) return false;
|
||||
DfgVertex*& nextpr = nextp[vtx];
|
||||
if (nextpr) return false;
|
||||
// Actually add to work list.
|
||||
nextInWorklistp = workListp;
|
||||
nextpr = workListp;
|
||||
workListp = &vtx;
|
||||
return false;
|
||||
};
|
||||
|
|
@ -462,9 +450,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
// Pick up the head of the work list
|
||||
DfgVertex* const vtxp = workListp;
|
||||
// Detach the head
|
||||
workListp = vtxp->getUser<DfgVertex*>();
|
||||
workListp = nextp.at(vtxp);
|
||||
// Reset user pointer so it can be added back to the work list later
|
||||
vtxp->setUser<DfgVertex*>(nullptr);
|
||||
nextp.at(vtxp) = nullptr;
|
||||
// Prefetch next item
|
||||
VL_PREFETCH_RW(workListp);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,14 +64,14 @@ void dfgToAst(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
|||
|
||||
// Construct binary to oneHot decoders
|
||||
void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED;
|
||||
// Sets DfgVertex::user<uint64_t>() for all vertext to:
|
||||
// Populates the given DfgUserMap for all vertext to:
|
||||
// - 0, if the vertex is not part of a non-trivial strongly connected component
|
||||
// and is not part of a self-loop. That is: the Vertex is not part of any cycle.
|
||||
// - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N.
|
||||
// That is: each set of vertices that are reachable from each other will have the same
|
||||
// non-zero value assigned.
|
||||
// Returns the number of non-trivial SCCs (distinct cycles)
|
||||
uint32_t colorStronglyConnectedComponents(DfgGraph&) VL_MT_DISABLED;
|
||||
uint32_t colorStronglyConnectedComponents(const DfgGraph&, DfgUserMap<uint64_t>&) VL_MT_DISABLED;
|
||||
// Common subexpression elimination
|
||||
void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED;
|
||||
// Inline fully driven variables
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The DfgGraph being visited
|
||||
// Map from vertex to next vertex on work list
|
||||
DfgUserMap<DfgVertex*> m_nextp = m_dfg.makeUserMap<DfgVertex*>();
|
||||
V3DfgPeepholeContext& m_ctx; // The config structure
|
||||
AstNodeDType* const m_bitDType = V3Dfg::dtypePacked(1); // Common, so grab it up front
|
||||
// Head of work list. Note that we want all next pointers in the list to be non-zero (including
|
||||
|
|
@ -156,9 +158,9 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// We only process actual operation vertices
|
||||
if (vtxp->is<DfgConst>() || vtxp->is<DfgVertexVar>()) return;
|
||||
// If already in work list then nothing to do
|
||||
if (vtxp->getUser<DfgVertex*>()) return;
|
||||
if (m_nextp[vtxp]) return;
|
||||
// Actually add to work list.
|
||||
vtxp->setUser<DfgVertex*>(m_workListp);
|
||||
m_nextp[vtxp] = m_workListp;
|
||||
m_workListp = vtxp;
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +183,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
addSourcesToWorkList(vtxp);
|
||||
// If in work list then we can't delete it just yet (as we can't remove from the middle of
|
||||
// the work list), but it will be deleted when the work list is processed.
|
||||
if (vtxp->getUser<DfgVertex*>()) return;
|
||||
if (m_nextp[vtxp]) return;
|
||||
// Otherwise we can delete it now.
|
||||
// Remove from cache
|
||||
m_cache.invalidateByValue(vtxp);
|
||||
|
|
@ -230,14 +232,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
Vertex* make(FileLine* flp, AstNodeDType* dtypep, Operands... operands) {
|
||||
// Find or create an equivalent vertex
|
||||
Vertex* const vtxp = m_cache.getOrCreate<Vertex, Operands...>(flp, dtypep, operands...);
|
||||
|
||||
// Add to work list.
|
||||
DfgVertex*& workListNextp = vtxp->template user<DfgVertex*>();
|
||||
if (!workListNextp) {
|
||||
workListNextp = m_workListp;
|
||||
m_workListp = vtxp;
|
||||
}
|
||||
|
||||
// Add to work list
|
||||
addToWorkList(vtxp);
|
||||
// Return new node
|
||||
return vtxp;
|
||||
}
|
||||
|
|
@ -1755,13 +1751,9 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
: m_dfg{dfg}
|
||||
, m_ctx{ctx} {
|
||||
|
||||
// DfgVertex::user is the next pointer of the work list elements
|
||||
const auto userDataInUse = m_dfg.userDataInUse();
|
||||
|
||||
// Add all vertices to the work list, and to the vertex cache.
|
||||
// This also allocates all DfgVertex::user.
|
||||
// Add all operation vertices to the work list and cache
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
vtx.setUser<DfgVertex*>(m_workListp);
|
||||
m_nextp[vtx] = m_workListp;
|
||||
m_workListp = &vtx;
|
||||
m_cache.cache(&vtx);
|
||||
}
|
||||
|
|
@ -1771,9 +1763,9 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// Pick up the head
|
||||
DfgVertex* const vtxp = m_workListp;
|
||||
// Detach the head and prefetch next
|
||||
m_workListp = vtxp->getUser<DfgVertex*>();
|
||||
m_workListp = m_nextp[vtxp];
|
||||
VL_PREFETCH_RW(m_workListp);
|
||||
vtxp->setUser<DfgVertex*>(nullptr);
|
||||
m_nextp[vtxp] = nullptr;
|
||||
// Remove unused vertices as we gp
|
||||
if (!vtxp->hasSinks()) {
|
||||
deleteVertex(vtxp);
|
||||
|
|
|
|||
|
|
@ -1822,8 +1822,8 @@ void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) {
|
|||
// Otherwise figure out which vertices are worth synthesizing.
|
||||
|
||||
// Find cycles
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
V3DfgPasses::colorStronglyConnectedComponents(dfg);
|
||||
DfgUserMap<uint64_t> scc = dfg.makeUserMap<uint64_t>();
|
||||
V3DfgPasses::colorStronglyConnectedComponents(dfg, scc);
|
||||
|
||||
// First, gather variables, we will then attempt to synthesize all their drivers
|
||||
std::vector<DfgVertexVar*> varps;
|
||||
|
|
@ -1832,7 +1832,7 @@ void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) {
|
|||
if (!var.srcp()) continue;
|
||||
|
||||
// Circular variable - synthesize
|
||||
if (var.getUser<uint64_t>()) {
|
||||
if (scc.at(var)) {
|
||||
varps.emplace_back(&var);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue