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