2024-03-06 19:01:52 +01:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Dfg vertex cache to find existing vertices
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2024-03-06 19:01:52 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// A cache for DfgGraph, to find existing vertices with identical inputs.
|
|
|
|
|
//
|
|
|
|
|
// Beware that if you use this 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#ifndef VERILATOR_V3DFGCACHE_H_
|
|
|
|
|
#define VERILATOR_V3DFGCACHE_H_
|
|
|
|
|
|
|
|
|
|
#include "V3Dfg.h"
|
|
|
|
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
|
|
// Template specializatoins must be defiend before they are used,
|
|
|
|
|
// so this is implemented in a namespace
|
|
|
|
|
|
|
|
|
|
namespace V3DfgCacheInternal {
|
|
|
|
|
|
|
|
|
|
// Hash constants by value, everything else by identity
|
|
|
|
|
inline V3Hash vertexHash(const DfgVertex* vtxp) {
|
|
|
|
|
if (const DfgConst* const constp = vtxp->cast<DfgConst>()) return constp->num().toHash();
|
|
|
|
|
return V3Hash{reinterpret_cast<uint64_t>(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<DfgConst>()) {
|
|
|
|
|
const DfgConst* const bConstp = bp->as<DfgConst>();
|
|
|
|
|
return aConstp->num().isCaseEq(bConstp->num());
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class KeySel final {
|
|
|
|
|
const DfgVertex* const m_fromp;
|
|
|
|
|
const uint32_t m_lsb;
|
2025-09-07 21:38:50 +02:00
|
|
|
const uint32_t m_size;
|
2024-03-06 19:01:52 +01:00
|
|
|
|
|
|
|
|
public:
|
2025-09-07 21:38:50 +02:00
|
|
|
KeySel(DfgVertex* fromp, uint32_t lsb, uint32_t size)
|
2024-03-06 19:01:52 +01:00
|
|
|
: m_fromp{fromp}
|
|
|
|
|
, m_lsb{lsb}
|
2025-09-07 21:38:50 +02:00
|
|
|
, m_size{size} {}
|
2024-03-06 19:01:52 +01:00
|
|
|
|
|
|
|
|
struct Hash final {
|
|
|
|
|
size_t operator()(const KeySel& key) const {
|
2024-06-14 03:29:03 +02:00
|
|
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
2024-03-06 19:01:52 +01:00
|
|
|
V3Hash hash{vertexHash(key.m_fromp)};
|
|
|
|
|
hash += key.m_lsb;
|
2025-09-07 21:38:50 +02:00
|
|
|
hash += key.m_size;
|
2024-03-06 19:01:52 +01:00
|
|
|
return hash.value();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Equal final {
|
|
|
|
|
bool operator()(const KeySel& a, const KeySel& b) const {
|
2025-09-07 21:38:50 +02:00
|
|
|
return a.m_lsb == b.m_lsb && a.m_size == b.m_size && vertexEqual(a.m_fromp, b.m_fromp);
|
2024-03-06 19:01:52 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class KeyUnary final {
|
|
|
|
|
const DfgVertex* const m_source0p;
|
|
|
|
|
|
|
|
|
|
public:
|
2024-06-14 03:29:03 +02:00
|
|
|
// cppcheck-suppress noExplicitConstructor
|
2024-03-06 19:01:52 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 {
|
2024-06-14 03:29:03 +02:00
|
|
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
2024-03-06 19:01:52 +01:00
|
|
|
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 {
|
2024-06-14 03:29:03 +02:00
|
|
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
2024-03-06 19:01:52 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_Key, typename T_Val>
|
|
|
|
|
using Cache = std::unordered_map<T_Key, T_Val, typename T_Key::Hash, typename T_Key::Equal>;
|
2024-03-06 19:01:52 +01:00
|
|
|
|
|
|
|
|
using CacheSel = Cache<KeySel, DfgSel*>;
|
|
|
|
|
using CacheUnary = Cache<KeyUnary, DfgVertexUnary*>;
|
|
|
|
|
using CacheBinary = Cache<KeyBinary, DfgVertexBinary*>;
|
|
|
|
|
using CacheTernary = Cache<KeyTernary, DfgVertexTernary*>;
|
|
|
|
|
|
|
|
|
|
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
2025-09-07 21:38:50 +02:00
|
|
|
inline DfgSel*& getEntry(CacheSel& cache, const DfgDataType& dtype, DfgVertex* src0p,
|
|
|
|
|
uint32_t lsb) {
|
2024-03-06 19:01:52 +01:00
|
|
|
return cache
|
|
|
|
|
.emplace(std::piecewise_construct, //
|
2025-09-07 21:38:50 +02:00
|
|
|
std::forward_as_tuple(src0p, lsb, dtype.size()), //
|
2024-03-06 19:01:52 +01:00
|
|
|
std::forward_as_tuple(nullptr))
|
|
|
|
|
.first->second;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-07 21:38:50 +02:00
|
|
|
inline DfgVertexUnary*& getEntry(CacheUnary& cache, const DfgDataType&, DfgVertex* src0p) {
|
2024-03-06 19:01:52 +01:00
|
|
|
return cache
|
|
|
|
|
.emplace(std::piecewise_construct, //
|
|
|
|
|
std::forward_as_tuple(src0p), //
|
|
|
|
|
std::forward_as_tuple(nullptr))
|
|
|
|
|
.first->second;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-07 21:38:50 +02:00
|
|
|
inline DfgVertexBinary*& getEntry(CacheBinary& cache, const DfgDataType&, DfgVertex* src0p,
|
2024-03-06 19:01:52 +01:00
|
|
|
DfgVertex* src1p) {
|
|
|
|
|
return cache
|
|
|
|
|
.emplace(std::piecewise_construct, //
|
|
|
|
|
std::forward_as_tuple(src0p, src1p), //
|
|
|
|
|
std::forward_as_tuple(nullptr))
|
|
|
|
|
.first->second;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-07 21:38:50 +02:00
|
|
|
inline DfgVertexTernary*& getEntry(CacheTernary& cache, const DfgDataType&, DfgVertex* src0p,
|
2024-03-06 19:01:52 +01:00
|
|
|
DfgVertex* src1p, DfgVertex* src2p) {
|
|
|
|
|
return cache
|
|
|
|
|
.emplace(std::piecewise_construct, //
|
|
|
|
|
std::forward_as_tuple(src0p, src1p, src2p), //
|
|
|
|
|
std::forward_as_tuple(nullptr))
|
|
|
|
|
.first->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
2025-09-07 21:38:50 +02:00
|
|
|
inline CacheSel::iterator find(CacheSel& cache, DfgVertex* src0p, uint32_t lsb, uint32_t size) {
|
|
|
|
|
return cache.find({src0p, lsb, size});
|
2024-03-06 19:01:52 +01:00
|
|
|
}
|
|
|
|
|
|
2025-09-07 21:38:50 +02:00
|
|
|
inline CacheUnary::iterator find(CacheUnary& cache, DfgVertex* src0p) {
|
2024-03-06 19:01:52 +01:00
|
|
|
return cache.find({src0p});
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-07 21:38:50 +02:00
|
|
|
inline CacheBinary::iterator find(CacheBinary& cache, DfgVertex* src0p, DfgVertex* src1p) {
|
2024-03-06 19:01:52 +01:00
|
|
|
return cache.find({src0p, src1p});
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-07 21:38:50 +02:00
|
|
|
inline CacheTernary::iterator find(CacheTernary& cache, DfgVertex* src0p, DfgVertex* src1p,
|
|
|
|
|
DfgVertex* src2p) {
|
2024-03-06 19:01:52 +01:00
|
|
|
return cache.find({src0p, src1p, src2p});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) { //
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->inputp(0, src0p);
|
2024-03-06 19:01:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) {
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->inputp(0, src0p);
|
|
|
|
|
vtxp->inputp(1, src1p);
|
2024-03-06 19:01:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p,
|
|
|
|
|
DfgVertex* src2p) {
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->inputp(0, src0p);
|
|
|
|
|
vtxp->inputp(1, src1p);
|
|
|
|
|
vtxp->inputp(2, src2p);
|
2024-03-06 19:01:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get or create (and insert) vertex with given operands
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename Vertex, typename T_Cache, typename... Operands>
|
2025-09-07 21:38:50 +02:00
|
|
|
inline Vertex* getOrCreate(DfgGraph& dfg, FileLine* flp, T_Cache& cache, const DfgDataType& dtype,
|
2024-03-06 19:01:52 +01:00
|
|
|
Operands... operands) {
|
2025-09-07 21:38:50 +02:00
|
|
|
typename T_Cache::mapped_type& entrypr = getEntry(cache, dtype, operands...);
|
2024-03-06 19:01:52 +01:00
|
|
|
if (!entrypr) {
|
2025-09-07 21:38:50 +02:00
|
|
|
Vertex* const newp = new Vertex{dfg, flp, dtype};
|
2024-03-06 19:01:52 +01:00
|
|
|
setOperands(newp, operands...);
|
|
|
|
|
entrypr = newp;
|
|
|
|
|
}
|
|
|
|
|
return reinterpret_cast<Vertex*>(entrypr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// These add an existing vertex to the table, if an equivalent does not yet exist
|
|
|
|
|
inline void cache(CacheSel& cache, DfgSel* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
DfgSel*& entrypr = getEntry(cache, vtxp->dtype(), vtxp->fromp(), vtxp->lsb());
|
2024-03-06 19:01:52 +01:00
|
|
|
if (!entrypr) entrypr = vtxp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void cache(CacheUnary& cache, DfgVertexUnary* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtype(), vtxp->inputp(0));
|
2024-03-06 19:01:52 +01:00
|
|
|
if (!entrypr) entrypr = vtxp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
DfgVertexBinary*& entrypr = getEntry(cache, vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1));
|
2024-03-06 19:01:52 +01:00
|
|
|
if (!entrypr) entrypr = vtxp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) {
|
|
|
|
|
DfgVertexTernary*& entrypr
|
2025-09-07 21:38:50 +02:00
|
|
|
= getEntry(cache, vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
|
2024-03-06 19:01:52 +01:00
|
|
|
if (!entrypr) entrypr = vtxp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// These remove an existing vertex from the cache, if it is the cached vertex
|
2025-08-20 19:21:24 +02:00
|
|
|
inline void invalidateByValue(CacheSel& cache, const DfgSel* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
const auto it = find(cache, vtxp->fromp(), vtxp->lsb(), vtxp->size());
|
2024-03-06 19:01:52 +01:00
|
|
|
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
const auto it = find(cache, vtxp->inputp(0));
|
2024-03-06 19:01:52 +01:00
|
|
|
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
const auto it = find(cache, vtxp->inputp(0), vtxp->inputp(1));
|
2024-03-06 19:01:52 +01:00
|
|
|
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
const auto it = find(cache, vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
|
2024-03-06 19:01:52 +01:00
|
|
|
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 <typename Vertex> struct CacheTypeImpl : public CacheTypeImpl<typename Vertex::Super> {};
|
|
|
|
|
template <> struct CacheTypeImpl<DfgSel> { using type = CacheSel; };
|
|
|
|
|
template <> struct CacheTypeImpl<DfgVertexUnary> { using type = CacheUnary; };
|
|
|
|
|
template <> struct CacheTypeImpl<DfgVertexBinary> { using type = CacheBinary; };
|
|
|
|
|
template <> struct CacheTypeImpl<DfgVertexTernary> { using type = CacheTernary; };
|
|
|
|
|
template <typename Vertex> using CacheType = typename CacheTypeImpl<Vertex>::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 {
|
|
|
|
|
DfgGraph& m_dfg; // The DfgGraph we are caching the vertices of
|
|
|
|
|
|
|
|
|
|
// The per type caches
|
|
|
|
|
#define VERTEX_CACHE_DECLARE_LUT(t) CacheType<t> m_cache##t;
|
|
|
|
|
FOREACH_CACHED_VERTEX_TYPE(VERTEX_CACHE_DECLARE_LUT)
|
|
|
|
|
#undef VERTEX_CACHE_DECLARE_LUT
|
|
|
|
|
|
|
|
|
|
// Specializations return one of the above m_cache members
|
|
|
|
|
template <typename Vertex>
|
|
|
|
|
inline CacheType<Vertex>& cacheForType();
|
|
|
|
|
|
|
|
|
|
public:
|
2024-06-14 03:29:03 +02:00
|
|
|
explicit V3DfgCache(DfgGraph& dfg)
|
2024-03-06 19:01:52 +01:00
|
|
|
: m_dfg{dfg} {}
|
|
|
|
|
|
|
|
|
|
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
|
|
|
|
|
template <typename Vertex, typename... Operands>
|
2025-09-07 21:38:50 +02:00
|
|
|
inline Vertex* getOrCreate(FileLine* flp, const DfgDataType& dtype, Operands... operands);
|
2024-03-06 19:01:52 +01:00
|
|
|
|
|
|
|
|
// Add an existing vertex of the table. If an equivalent already exists, then nothing happens.
|
|
|
|
|
void cache(DfgVertex* vtxp);
|
|
|
|
|
|
|
|
|
|
// Remove an exiting vertex, it is the cached vertex.
|
|
|
|
|
void invalidateByValue(DfgVertex* vtxp);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
|
// The per-type specializations of 'V3DfgCache::cacheForType'
|
|
|
|
|
#define VERTEX_CACHE_DEFINE_LUT_SPECIALIZATION(t) \
|
|
|
|
|
template <> inline CacheType<t>& V3DfgCache::cacheForType<t>() { 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 <typename Vertex, typename... Operands>
|
2025-09-07 21:38:50 +02:00
|
|
|
Vertex* V3DfgCache::getOrCreate(FileLine* flp, const DfgDataType& dtype, Operands... operands) {
|
2024-03-06 19:01:52 +01:00
|
|
|
static_assert(std::is_final<Vertex>::value, "Must invoke on final vertex type");
|
|
|
|
|
constexpr bool isSel = std::is_same<DfgSel, Vertex>::value;
|
|
|
|
|
constexpr bool isUnary = !isSel && std::is_base_of<DfgVertexUnary, Vertex>::value;
|
|
|
|
|
constexpr bool isBinary = std::is_base_of<DfgVertexBinary, Vertex>::value;
|
|
|
|
|
constexpr bool isTernary = std::is_base_of<DfgVertexTernary, Vertex>::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,
|
2025-09-07 21:38:50 +02:00
|
|
|
"Wrong number of operands to DfgVertexUnary");
|
2024-03-06 19:01:52 +01:00
|
|
|
static_assert(!isBinary || sizeof...(Operands) == 2,
|
2025-09-07 21:38:50 +02:00
|
|
|
"Wrong number of operands to DfgVertexBinary");
|
2024-03-06 19:01:52 +01:00
|
|
|
static_assert(!isTernary || sizeof...(Operands) == 3,
|
2025-09-07 21:38:50 +02:00
|
|
|
"Wrong number of operands to DfgVertexTernary");
|
2024-03-06 19:01:52 +01:00
|
|
|
|
|
|
|
|
return V3DfgCacheInternal::getOrCreate<Vertex, CacheType<Vertex>, Operands...>(
|
2025-09-07 21:38:50 +02:00
|
|
|
m_dfg, flp, cacheForType<Vertex>(), dtype, operands...);
|
2024-03-06 19:01:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace V3DfgCacheInternal
|
|
|
|
|
|
|
|
|
|
// Export only the public interface class
|
|
|
|
|
using V3DfgCacheInternal::V3DfgCache;
|
|
|
|
|
|
|
|
|
|
#endif // VERILATOR_V3DFGCACHE_H_
|