From b9c8560d123bac2d6e04896c5a3e98107a2de8b8 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 22 Mar 2026 10:19:31 +0000 Subject: [PATCH] Internals: Simplify V3DfgCache --- src/CMakeLists.txt | 1 - src/Makefile_obj.in | 1 - src/V3DfgCache.cpp | 53 ---- src/V3DfgCache.h | 699 +++++++++++++++++++----------------------- src/V3DfgDataType.h | 8 + src/V3DfgPeephole.cpp | 13 +- src/astgen | 7 + 7 files changed, 334 insertions(+), 448 deletions(-) delete mode 100644 src/V3DfgCache.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b1f6f8ab..d03324eed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -244,7 +244,6 @@ set(COMMON_SOURCES V3Dfg.cpp V3DfgAstToDfg.cpp V3DfgBreakCycles.cpp - V3DfgCache.cpp V3DfgColorSCCs.cpp V3DfgCse.cpp V3DfgDataType.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index b2047d216..855e0835f 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -258,7 +258,6 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Dfg.o \ V3DfgAstToDfg.o \ V3DfgBreakCycles.o \ - V3DfgCache.o \ V3DfgColorSCCs.o \ V3DfgCse.o \ V3DfgDataType.o \ diff --git a/src/V3DfgCache.cpp b/src/V3DfgCache.cpp deleted file mode 100644 index d6816bae7..000000000 --- a/src/V3DfgCache.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Dfg vertex cache to find existing vertices -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of either the GNU Lesser General Public License Version 3 -// or the Perl Artistic License Version 2.0. -// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* -// -// A cache for DfgGraph, to find existing vertices with identical inputs. -// -// Beware that if you use data-structure, you must invalidate the cache any -// time you change the inputs of an existing vertex, otherwise you will -// have a very bad day. -// -//************************************************************************* - -#include "V3DfgCache.h" - -namespace V3DfgCacheInternal { - -DfgVertex* V3DfgCache::cache(DfgVertex* vtxp) { - switch (vtxp->type()) { -#define VERTEX_CACHE_ADD_CASE(t) \ - case t::dfgType(): \ - return V3DfgCacheInternal::cache(m_cache##t, reinterpret_cast(vtxp)); \ - break; - FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_ADD_CASE) -#undef VERTEX_CACHE_ADD_CASE - default: return nullptr; - } -} - -void V3DfgCache::invalidateByValue(DfgVertex* vtxp) { - switch (vtxp->type()) { -#define VERTEX_CACHE_ADD_CASE(t) \ - case t::dfgType(): \ - V3DfgCacheInternal::invalidateByValue(m_cache##t, reinterpret_cast(vtxp)); \ - break; - FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_ADD_CASE) -#undef VERTEX_CACHE_ADD_CASE - default: break; - } -} - -} // namespace V3DfgCacheInternal diff --git a/src/V3DfgCache.h b/src/V3DfgCache.h index 252db7340..f133091f0 100644 --- a/src/V3DfgCache.h +++ b/src/V3DfgCache.h @@ -26,413 +26,342 @@ #define VERILATOR_V3DFGCACHE_H_ #include "V3Dfg.h" +#include "V3DfgDataType.h" -#include +#include -// Template specializatoins must be defiend before they are used, -// so this is implemented in a namespace +// Type predicate true for cached vertex types +template +using V3DfgCacheIsCached + = std::integral_constant::value + || std::is_base_of::value + || std::is_base_of::value>; -namespace V3DfgCacheInternal { - -// Hash constants by value, everything else by identity -inline V3Hash vertexHash(const DfgVertex* vtxp) { - if (const DfgConst* const constp = vtxp->cast()) return constp->num().toHash(); - return V3Hash{reinterpret_cast(vtxp)}; -} - -// Constants are equal by value, everything else is equal by identity -inline bool vertexEqual(const DfgVertex* ap, const DfgVertex* bp) { - if (ap == bp) return true; - if (ap->type() != bp->type()) return false; - if (const DfgConst* const aConstp = ap->cast()) { - const DfgConst* const bConstp = bp->as(); - return aConstp->num().isCaseEq(bConstp->num()); - } - return false; -} - -class KeySel final { - const DfgVertex* const m_fromp; - const uint32_t m_lsb; - const uint32_t m_size; - -public: - KeySel(DfgVertex* fromp, uint32_t lsb, uint32_t size) - : m_fromp{fromp} - , m_lsb{lsb} - , m_size{size} {} - - struct Hash final { - size_t operator()(const KeySel& key) const { - // cppcheck-suppress unreadVariable // cppcheck bug - V3Hash hash{vertexHash(key.m_fromp)}; - hash += key.m_lsb; - hash += key.m_size; - return hash.value(); - } - }; - - struct Equal final { - bool operator()(const KeySel& a, const KeySel& b) const { - return a.m_lsb == b.m_lsb && a.m_size == b.m_size && vertexEqual(a.m_fromp, b.m_fromp); - } - }; +// Helper template to determine the cache type for a vertex type +template +struct V3DfgCacheType final { + using Type = CacheBase; }; -class KeyUnary final { - const DfgVertex* const m_source0p; - -public: - // cppcheck-suppress noExplicitConstructor - KeyUnary(DfgVertex* source0p) - : m_source0p{source0p} {} - - struct Hash final { - size_t operator()(const KeyUnary& key) const { // - return vertexHash(key.m_source0p).value(); - } - }; - - struct Equal final { - bool operator()(const KeyUnary& a, const KeyUnary& b) const { - return vertexEqual(a.m_source0p, b.m_source0p); - } - }; +template +struct V3DfgCacheType final { + using Type = std::conditional_t::value, Cache, + typename V3DfgCacheType::Type>; }; -class KeyBinary final { - const DfgVertex* const m_source0p; - const DfgVertex* const m_source1p; - -public: - KeyBinary(DfgVertex* source0p, DfgVertex* source1p) - : m_source0p{source0p} - , m_source1p{source1p} {} - - struct Hash final { - size_t operator()(const KeyBinary& key) const { - // cppcheck-suppress unreadVariable // cppcheck bug - V3Hash hash{vertexHash(key.m_source0p)}; - hash += vertexHash(key.m_source1p); - return hash.value(); - } - }; - - struct Equal final { - bool operator()(const KeyBinary& a, const KeyBinary& b) const { - return vertexEqual(a.m_source0p, b.m_source0p) - && vertexEqual(a.m_source1p, b.m_source1p); - } - }; -}; - -class KeyTernary final { - const DfgVertex* const m_source0p; - const DfgVertex* const m_source1p; - const DfgVertex* const m_source2p; - -public: - KeyTernary(DfgVertex* source0p, DfgVertex* source1p, DfgVertex* source2p) - : m_source0p{source0p} - , m_source1p{source1p} - , m_source2p{source2p} {} - - struct Hash final { - size_t operator()(const KeyTernary& key) const { - // cppcheck-suppress unreadVariable // cppcheck bug - V3Hash hash{vertexHash(key.m_source0p)}; - hash += vertexHash(key.m_source1p); - hash += vertexHash(key.m_source2p); - return hash.value(); - } - }; - - struct Equal final { - bool operator()(const KeyTernary& a, const KeyTernary& b) const { - return vertexEqual(a.m_source0p, b.m_source0p) - && vertexEqual(a.m_source1p, b.m_source1p) - && vertexEqual(a.m_source2p, b.m_source2p); - } - }; -}; - -template -using Cache = std::unordered_map; - -using CacheSel = Cache; -using CacheUnary = Cache; -using CacheBinary = Cache; -using CacheTernary = Cache; - -// These return a reference to the mapped entry, inserting a nullptr if not yet exists -inline DfgSel*& getEntry(CacheSel& cache, const DfgDataType& dtype, DfgVertex* src0p, - uint32_t lsb) { - const KeySel key{src0p, lsb, dtype.size()}; - return cache[key]; -} - -inline DfgVertexUnary*& getEntry(CacheUnary& cache, const DfgDataType&, DfgVertex* src0p) { - const KeyUnary key{src0p}; - return cache[key]; -} - -inline DfgVertexBinary*& getEntry(CacheBinary& cache, const DfgDataType&, DfgVertex* src0p, - DfgVertex* src1p) { - const KeyBinary key{src0p, src1p}; - return cache[key]; -} - -inline DfgVertexTernary*& getEntry(CacheTernary& cache, const DfgDataType&, DfgVertex* src0p, - DfgVertex* src1p, DfgVertex* src2p) { - const KeyTernary key{src0p, src1p, src2p}; - return cache[key]; -} - -// These return an iterator which might be cache.end() if not contained -inline CacheSel::iterator find(CacheSel& cache, const DfgDataType& dtype, DfgVertex* src0p, - uint32_t lsb) { - const KeySel key{src0p, lsb, dtype.size()}; - return cache.find(key); -} - -inline CacheUnary::iterator find(CacheUnary& cache, const DfgDataType&, DfgVertex* src0p) { - const KeyUnary key{src0p}; - return cache.find(key); -} - -inline CacheBinary::iterator find(CacheBinary& cache, const DfgDataType&, DfgVertex* src0p, - DfgVertex* src1p) { - const KeyBinary key{src0p, src1p}; - return cache.find(key); -} - -inline CacheTernary::iterator find(CacheTernary& cache, const DfgDataType&, DfgVertex* src0p, - DfgVertex* src1p, DfgVertex* src2p) { - const KeyTernary key{src0p, src1p, src2p}; - return cache.find(key); -} - -// These set the operands of a new vertex -inline void setOperands(DfgSel* vtxp, DfgVertex* fromp, uint32_t lsb) { - vtxp->fromp(fromp); - vtxp->lsb(lsb); -} - -inline void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { // - vtxp->inputp(0, src0p); -} - -inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) { - vtxp->inputp(0, src0p); - vtxp->inputp(1, src1p); -} - -inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p, - DfgVertex* src2p) { - vtxp->inputp(0, src0p); - vtxp->inputp(1, src1p); - vtxp->inputp(2, src2p); -} - -// Get or create (and insert) vertex with given operands -template -inline Vertex* getOrCreate(DfgGraph& dfg, FileLine* flp, T_Cache& cache, const DfgDataType& dtype, - Operands... operands) { - typename T_Cache::mapped_type& entrypr = getEntry(cache, dtype, operands...); - if (!entrypr) { - Vertex* const newp = new Vertex{dfg, flp, dtype}; - setOperands(newp, operands...); - entrypr = newp; - } - return reinterpret_cast(entrypr); -} - -// Get vertex with given operands, return nullptr if not in cache -template -inline Vertex* get(DfgGraph& dfg, T_Cache& cache, const DfgDataType& dtype, Operands... operands) { - const auto it = find(cache, dtype, operands...); - return it != cache.end() ? reinterpret_cast(it->second) : nullptr; -} - -// These add an existing vertex to the table. If an equivalent exists, -// it is returned and the cache is not updated. -inline DfgSel* cache(CacheSel& cache, DfgSel* vtxp) { - DfgSel*& entrypr = getEntry(cache, vtxp->dtype(), vtxp->fromp(), vtxp->lsb()); - if (entrypr && entrypr != vtxp) return entrypr; - entrypr = vtxp; - return nullptr; -} - -inline DfgVertexUnary* cache(CacheUnary& cache, DfgVertexUnary* vtxp) { - DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtype(), vtxp->inputp(0)); - if (entrypr && entrypr != vtxp) return entrypr; - entrypr = vtxp; - return nullptr; -} - -inline DfgVertexBinary* cache(CacheBinary& cache, DfgVertexBinary* vtxp) { - DfgVertexBinary*& entrypr = getEntry(cache, vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1)); - if (entrypr && entrypr != vtxp) return entrypr; - entrypr = vtxp; - return nullptr; -} - -inline DfgVertexTernary* cache(CacheTernary& cache, DfgVertexTernary* vtxp) { - DfgVertexTernary*& entrypr - = getEntry(cache, vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2)); - if (entrypr && entrypr != vtxp) return entrypr; - entrypr = vtxp; - return nullptr; -} - -// These remove an existing vertex from the cache, if it is the cached vertex -inline void invalidateByValue(CacheSel& cache, const DfgSel* vtxp) { - const auto it = find(cache, vtxp->dtype(), vtxp->fromp(), vtxp->lsb()); - if (it != cache.end() && it->second == vtxp) cache.erase(it); -} - -inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* vtxp) { - const auto it = find(cache, vtxp->dtype(), vtxp->inputp(0)); - if (it != cache.end() && it->second == vtxp) cache.erase(it); -} - -inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) { - const auto it = find(cache, vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1)); - if (it != cache.end() && it->second == vtxp) cache.erase(it); -} - -inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) { - const auto it = find(cache, vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2)); - if (it != cache.end() && it->second == vtxp) cache.erase(it); -} - -// clang-format off - -// This type defines the cache type corresponding to a vertex type -template struct CacheTypeImpl : public CacheTypeImpl {}; -template <> struct CacheTypeImpl { using type = CacheSel; }; -template <> struct CacheTypeImpl { using type = CacheUnary; }; -template <> struct CacheTypeImpl { using type = CacheBinary; }; -template <> struct CacheTypeImpl { using type = CacheTernary; }; -template using CacheType = typename CacheTypeImpl::type; - -// Just add new lines in here for new vertex types you need to be able to cache - -#define FOREACH_CACHED_VERTEX_TYPE(macro) \ - macro(DfgSel) \ - macro(DfgNot) \ - macro(DfgNegate) \ - macro(DfgConcat) \ - macro(DfgNeq) \ - macro(DfgShiftL) \ - macro(DfgShiftR) \ - macro(DfgShiftRS) \ - macro(DfgAdd) \ - macro(DfgSub) \ - macro(DfgMul) \ - macro(DfgMulS) \ - macro(DfgEq) \ - macro(DfgAnd) \ - macro(DfgOr) \ - macro(DfgXor) \ - macro(DfgRedAnd) \ - macro(DfgRedOr) \ - macro(DfgRedXor) \ - macro(DfgCond) - -// clang-format on - class V3DfgCache final { + // TYPES + class KeySel final { + const DfgDataType& m_dtype; + const DfgVertex* const m_fromp; + const uint32_t m_lsb; + + public: + KeySel(const DfgDataType& dtype, DfgVertex* fromp, uint32_t lsb) + : m_dtype{dtype} + , m_fromp{fromp} + , m_lsb{lsb} {} + KeySel(const DfgSel* vtxp) + : m_dtype{vtxp->dtype()} + , m_fromp{vtxp->fromp()} + , m_lsb{vtxp->lsb()} {} + + struct Hash final { + size_t operator()(const KeySel& key) const { + // cppcheck-suppress unreadVariable // cppcheck bug + V3Hash hash = key.m_dtype.hash(); + hash += vertexHash(key.m_fromp); + hash += key.m_lsb; + return hash.value(); + } + }; + + struct Equal final { + bool operator()(const KeySel& a, const KeySel& b) const { + return a.m_lsb == b.m_lsb && a.m_dtype == b.m_dtype + && vertexEqual(a.m_fromp, b.m_fromp); + } + }; + }; + + class KeyUnary final { + const DfgDataType& m_dtype; + const DfgVertex* const m_source0p; + + public: + // cppcheck-suppress noExplicitConstructor + KeyUnary(const DfgDataType& dtype, DfgVertex* source0p) + : m_dtype{dtype} + , m_source0p{source0p} {} + KeyUnary(const DfgVertexUnary* vtxp) + : m_dtype{vtxp->dtype()} + , m_source0p{vtxp->inputp(0)} {} + + struct Hash final { + size_t operator()(const KeyUnary& key) const { // + V3Hash hash = key.m_dtype.hash(); + hash += vertexHash(key.m_source0p); + return hash.value(); + } + }; + + struct Equal final { + bool operator()(const KeyUnary& a, const KeyUnary& b) const { + return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p); + } + }; + }; + + class KeyBinary final { + const DfgDataType& m_dtype; + const DfgVertex* const m_source0p; + const DfgVertex* const m_source1p; + + public: + KeyBinary(const DfgDataType& dtype, DfgVertex* source0p, DfgVertex* source1p) + : m_dtype{dtype} + , m_source0p{source0p} + , m_source1p{source1p} {} + KeyBinary(const DfgVertexBinary* vtxp) + : m_dtype{vtxp->dtype()} + , m_source0p{vtxp->inputp(0)} + , m_source1p{vtxp->inputp(1)} {} + + struct Hash final { + size_t operator()(const KeyBinary& key) const { + V3Hash hash = key.m_dtype.hash(); + hash += vertexHash(key.m_source0p); + hash += vertexHash(key.m_source1p); + return hash.value(); + } + }; + + struct Equal final { + bool operator()(const KeyBinary& a, const KeyBinary& b) const { + return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p) + && vertexEqual(a.m_source1p, b.m_source1p); + } + }; + }; + + class KeyTernary final { + const DfgDataType& m_dtype; + const DfgVertex* const m_source0p; + const DfgVertex* const m_source1p; + const DfgVertex* const m_source2p; + + public: + KeyTernary(const DfgDataType& dtype, DfgVertex* source0p, DfgVertex* source1p, + DfgVertex* source2p) + : m_dtype{dtype} + , m_source0p{source0p} + , m_source1p{source1p} + , m_source2p{source2p} {} + KeyTernary(const DfgVertexTernary* vtxp) + : m_dtype{vtxp->dtype()} + , m_source0p{vtxp->inputp(0)} + , m_source1p{vtxp->inputp(1)} + , m_source2p{vtxp->inputp(2)} {} + + struct Hash final { + size_t operator()(const KeyTernary& key) const { + V3Hash hash = key.m_dtype.hash(); + hash += vertexHash(key.m_source0p); + hash += vertexHash(key.m_source1p); + hash += vertexHash(key.m_source2p); + return hash.value(); + } + }; + + struct Equal final { + bool operator()(const KeyTernary& a, const KeyTernary& b) const { + return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p) + && vertexEqual(a.m_source1p, b.m_source1p) + && vertexEqual(a.m_source2p, b.m_source2p); + } + }; + }; + + class CacheBase VL_NOT_FINAL { + protected: + // These set the operands of a new vertex + static void setOperands(DfgSel* vtxp, DfgVertex* fromp, uint32_t lsb) { + vtxp->fromp(fromp); + vtxp->lsb(lsb); + } + + static void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { // + vtxp->inputp(0, src0p); + } + + static void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) { + vtxp->inputp(0, src0p); + vtxp->inputp(1, src1p); + } + + static void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p, + DfgVertex* src2p) { + vtxp->inputp(0, src0p); + vtxp->inputp(1, src1p); + vtxp->inputp(2, src2p); + } + + public: + // CacheBase does not cache anything + virtual DfgVertex* cache(DfgVertex*) { return nullptr; } + virtual void invalidate(const DfgVertex*) { return; } + }; + + template + class Cache final : public CacheBase { + static_assert(std::is_base_of::value, "T_Vertex must be a DfgVertex"); + // TYPES + using Hash = typename T_Key::Hash; + using Equal = typename T_Key::Equal; + using Map = std::unordered_map; + + // STATE + Map m_map; + + // METHODS + + // These return a reference to the mapped entry, inserting a nullptr if not yet exists + + template + T_Vertex*& entry(T_Args&&... args) { + const T_Key key{std::forward(args)...}; + return m_map[key]; + } + template + typename Map::iterator find(T_Args&&... args) { + const T_Key key{std::forward(args)...}; + return m_map.find(key); + } + + public: + // Add an existing vertex to the cache. If an equivalent exists, + // it is returned and the cache is not updated. + DfgVertex* cache(DfgVertex* vtxp) override { + UASSERT_OBJ(vtxp->is(), vtxp, "Vertex is wrong type"); + T_Vertex*& entrypr = entry(static_cast(vtxp)); + if (entrypr && entrypr != vtxp) return entrypr; + entrypr = static_cast(vtxp); + return nullptr; + } + // Remove an existing vertex from the cache, if it is the cached vertex, otherwise no-op + void invalidate(const DfgVertex* vtxp) override { + UASSERT_OBJ(vtxp->is(), vtxp, "Vertex is wrong type"); + const auto it = find(static_cast(vtxp)); + if (it != m_map.end() && it->second == vtxp) m_map.erase(it); + } + + // Get vertex with given operands, return nullptr if not in cache + template + Vertex* get(const DfgDataType& dtype, Operands... operands) { + const auto it = find(dtype, operands...); + return it != m_map.end() ? static_cast(it->second) : nullptr; + } + + // Get or create (and insert) vertex with given operands + template + Vertex* getOrCreate(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype, + Operands... operands) { + T_Vertex*& entryr = entry(dtype, operands...); + if (!entryr) { + T_Vertex* const newp = new Vertex{dfg, flp, dtype}; + setOperands(newp, operands...); + entryr = newp; + } + return static_cast(entryr); + } + }; + + // Map from Vertex type to cache type + template + using CacheType = + typename V3DfgCacheType, // + DfgVertexUnary, Cache, // + DfgVertexBinary, Cache, // + DfgVertexTernary, Cache // + >::Type; + // STATE DfgGraph& m_dfg; // The DfgGraph we are caching the vertices of // The per type caches -#define VERTEX_CACHE_DECLARE_LUT(t) CacheType m_cache##t; - FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_DECLARE_LUT) -#undef VERTEX_CACHE_DECLARE_LUT +#define VERTEX_CACHE_DECLARE_CACHE(t) CacheType m_cache##t; + FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE) +#undef VERTEX_CACHE_DECLARE_CACHE - // Specializations return one of the above m_cache members + // Map from vertex type to m_cache* instances for dynamic lookup + std::array m_vtxType2Cachep{}; + + // METHODS + + // Map from vertex type to m_cache* instances for static lookup template - inline CacheType& cacheForType(); + CacheType* cacheForType() { +#define VERTEX_CACHE_DECLARE_CACHE(t) \ + if VL_CONSTEXPR_CXX17 (std::is_same::value) \ + return reinterpret_cast*>(&m_cache##t); + FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE) +#undef VERTEX_CACHE_DECLARE_CACHE + return nullptr; // LCOV_EXCL_LINE + } + + // Hash constants by value, everything else by identity + static V3Hash vertexHash(const DfgVertex* vtxp) { + if (const DfgConst* const constp = vtxp->cast()) return constp->num().toHash(); + return V3Hash{reinterpret_cast(vtxp)}; + } + + // Constants are equal by value, everything else is equal by identity + static bool vertexEqual(const DfgVertex* ap, const DfgVertex* bp) { + if (ap == bp) return true; + if (ap->type() != bp->type()) return false; + if (const DfgConst* const aConstp = ap->cast()) { + const DfgConst* const bConstp = bp->as(); + return aConstp->num().isCaseEq(bConstp->num()); + } + return false; + } public: explicit V3DfgCache(DfgGraph& dfg) - : m_dfg{dfg} {} + : m_dfg{dfg} { + // Initialize the type to cache lookup table +#define VERTEX_CACHE_DECLARE_CACHE_PTR(t) m_vtxType2Cachep[t::dfgType()] = &m_cache##t; + FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE_PTR) +#undef VERTEX_CACHE_DECLARE_CACHE_PTR + + // Add all operation vertices to the cache + for (DfgVertex& vtx : m_dfg.opVertices()) cache(&vtx); + } + + // Add an existing vertex to the cache. If an equivalent already exists, + // it is returned and the cache is not updated. + DfgVertex* cache(DfgVertex* vtxp) { return m_vtxType2Cachep[vtxp->type()]->cache(vtxp); } + + // Remove an exiting vertex, it is the cached vertex. + void invalidate(DfgVertex* vtxp) { m_vtxType2Cachep[vtxp->type()]->invalidate(vtxp); } // Find a vertex of type 'Vertex', with the given operands, or create a new one and add it. template - inline Vertex* getOrCreate(FileLine* flp, const DfgDataType& dtype, Operands... operands); + Vertex* getOrCreate(FileLine* flp, const DfgDataType& dtype, Operands... operands) { + static_assert(std::is_final::value, "Must invoke on final vertex type"); + static_assert(V3DfgCacheIsCached::value, "Not a cached vertex type"); + return cacheForType()->template getOrCreate(m_dfg, flp, dtype, + operands...); + } // Find a vertex of type 'Vertex', with the given operands, return nullptr if not in cache. template - inline Vertex* get(const DfgDataType& dtype, Operands... operands); - - // Add an existing vertex of the cache. If an equivalent already exists, - // it is returned and the cache is not updated. - DfgVertex* cache(DfgVertex* vtxp); - - // Remove an exiting vertex, it is the cached vertex. - void invalidateByValue(DfgVertex* vtxp); + Vertex* get(const DfgDataType& dtype, Operands... operands) { + static_assert(std::is_final::value, "Must invoke on final vertex type"); + static_assert(V3DfgCacheIsCached::value, "Not a cached vertex type"); + return cacheForType()->template get(dtype, operands...); + } }; -// clang-format off -// The per-type specializations of 'V3DfgCache::cacheForType' -#define VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION(t) \ - template <> inline CacheType& V3DfgCache::cacheForType() { return m_cache ## t; } -FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION) -#undef VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION -// clang-format on - -// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it -template -Vertex* V3DfgCache::getOrCreate(FileLine* flp, const DfgDataType& dtype, Operands... operands) { - static_assert(std::is_final::value, "Must invoke on final vertex type"); - constexpr bool isSel = std::is_same::value; - constexpr bool isUnary = !isSel && std::is_base_of::value; - constexpr bool isBinary = std::is_base_of::value; - constexpr bool isTernary = std::is_base_of::value; - static_assert(isSel || isUnary || isBinary || isTernary, - "'get' called with unknown vertex type"); - - static_assert(!isSel || sizeof...(Operands) == 2, // - "Wrong number of operands to DfgSel"); - static_assert(!isUnary || sizeof...(Operands) == 1, - "Wrong number of operands to DfgVertexUnary"); - static_assert(!isBinary || sizeof...(Operands) == 2, - "Wrong number of operands to DfgVertexBinary"); - static_assert(!isTernary || sizeof...(Operands) == 3, - "Wrong number of operands to DfgVertexTernary"); - - return V3DfgCacheInternal::getOrCreate, Operands...>( - m_dfg, flp, cacheForType(), dtype, operands...); -} - -// Find a vertex of type 'Vertex', with the given operands, return nullptr if not in cache. -template -Vertex* V3DfgCache::get(const DfgDataType& dtype, Operands... operands) { - static_assert(std::is_final::value, "Must invoke on final vertex type"); - constexpr bool isSel = std::is_same::value; - constexpr bool isUnary = !isSel && std::is_base_of::value; - constexpr bool isBinary = std::is_base_of::value; - constexpr bool isTernary = std::is_base_of::value; - static_assert(isSel || isUnary || isBinary || isTernary, - "'get' called with unknown vertex type"); - - static_assert(!isSel || sizeof...(Operands) == 2, // - "Wrong number of operands to DfgSel"); - static_assert(!isUnary || sizeof...(Operands) == 1, - "Wrong number of operands to DfgVertexUnary"); - static_assert(!isBinary || sizeof...(Operands) == 2, - "Wrong number of operands to DfgVertexBinary"); - static_assert(!isTernary || sizeof...(Operands) == 3, - "Wrong number of operands to DfgVertexTernary"); - - return V3DfgCacheInternal::get, Operands...>( - m_dfg, cacheForType(), dtype, operands...); -} - -} // namespace V3DfgCacheInternal - -// Export only the public interface class -using V3DfgCacheInternal::V3DfgCache; - #endif // VERILATOR_V3DFGCACHE_H_ diff --git a/src/V3DfgDataType.h b/src/V3DfgDataType.h index 6fbf6d382..729b4c0b9 100644 --- a/src/V3DfgDataType.h +++ b/src/V3DfgDataType.h @@ -50,6 +50,7 @@ #include "V3Ast.h" #include "V3Error.h" #include "V3Global.h" +#include "V3Hash.h" #include #include @@ -131,6 +132,13 @@ public: return *m_elemDtypep; } + V3Hash hash() const { + V3Hash hash{static_cast(m_kind)}; + hash += m_size; + if (m_elemDtypep) hash += m_elemDtypep->hash(); + return hash; + } + //----------------------------------------------------------------------- // Static factory and management functions diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index f439551cb..3499d37e0 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -208,9 +208,9 @@ class V3DfgPeephole final : public DfgVisitor { // Add all sources to the work list addSourcesToWorkList(vtxp); // Remove this and sinks from cache - m_cache.invalidateByValue(vtxp); + m_cache.invalidate(vtxp); vtxp->foreachSink([&](DfgVertex& sink) { - m_cache.invalidateByValue(&sink); + m_cache.invalidate(&sink); return false; }); // Replace vertex with the replacement @@ -1441,7 +1441,7 @@ class V3DfgPeephole final : public DfgVisitor { DfgVertex* const newLhsp = lWidth == lhsp->width() ? lhsp - : make(flp, DfgDataType::packed(lWidth), lhsp, 0); + : make(flp, DfgDataType::packed(lWidth), lhsp, 0U); // Create the new concatenation DfgConcat* const newConcat = make( @@ -1905,10 +1905,7 @@ class V3DfgPeephole final : public DfgVisitor { for (DfgVertexVar& vtx : m_dfg.varVertices()) addToWorkList(&vtx); // Add all operation vertices to the work list and cache - for (DfgVertex& vtx : m_dfg.opVertices()) { - addToWorkList(&vtx); - m_cache.cache(&vtx); - } + for (DfgVertex& vtx : m_dfg.opVertices()) addToWorkList(&vtx); // Process the work list m_workList.foreach([&](DfgVertex& vtx) { @@ -1916,7 +1913,7 @@ class V3DfgPeephole final : public DfgVisitor { if (!vtx.hasSinks() && !vtx.is()) { if (vtx.nInputs()) { addSourcesToWorkList(&vtx); - m_cache.invalidateByValue(&vtx); + m_cache.invalidate(&vtx); vtx.resetInputs(); } deleteVertex(&vtx); diff --git a/src/astgen b/src/astgen index 3d26c0474..03fc303d7 100755 --- a/src/astgen +++ b/src/astgen @@ -1234,6 +1234,13 @@ def write_dfg_macros(filename): fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon + fh.write("\n\n#define FOREACH_DFG_VERTEX_TYPE(macro) \\\n") + for node in DfgVertexList: + if not node.isLeaf: + continue + fh.write(f" macro(Dfg{node.name}) \\\n") + fh.write("\n") + def write_dfg_auto_classes(filename): with open_file(filename) as fh: