mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 18:13:50 +02:00
The constant pool is now an ordinary package, created with the netlist and instantiated under $root like any other package, and most special handling has been removed, including AstConstPool. Lookups go through V3ConstPool, which has a singleton instance owned by V3Global. Static methods on V3Common form the public interfce to add/find constant pool entries. With that, the constant pool is usable at any stage during compilation, so enum and dimension tables created by V3Width, and enum value tables created by V3Randomize now also live in the constant pool instead of $unit, so identical tables are shared. Associative array constants are handled separately from unpacked tables, which used to be broken but unused. Emitted constant pool variables use direct initialization, and `constinit` with C++20 where the type allows it. This ensures we don't change a run-time in a way that would result in unintended code size increase. -fno-merge-const-pool, which was introduced years ago but never prompted a bug report is deprecated and has no effect. This is also prep for future work.
164 lines
7.5 KiB
C++
164 lines
7.5 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Constant pool
|
|
//
|
|
// 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: 2026 Wilson Snyder
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// V3ConstPool finds or creates entries in the constant pool package
|
|
// (AstNetlist::constPoolPkgp()), which holds read-only static data, i.e.:
|
|
// constants, maps and tables, shared across the whole design. Entries are
|
|
// ordinary package variables, deduplicated by value.
|
|
//
|
|
//*************************************************************************
|
|
|
|
#ifndef VERILATOR_V3CONSTPOOL_H_
|
|
#define VERILATOR_V3CONSTPOOL_H_
|
|
#include "config_build.h"
|
|
#include "verilatedos.h"
|
|
|
|
#include "V3Ast.h"
|
|
#include "V3HashTable.h"
|
|
#include "V3Hasher.h"
|
|
|
|
//============================================================================
|
|
|
|
class V3ConstPool final {
|
|
friend class V3Global; // Owns the instance, needs constructor access
|
|
|
|
// TYPES
|
|
// Scoping stage of the design, which determines how entries are referenced
|
|
enum class Stage : uint8_t {
|
|
UNSCOPED, // Before V3Scope: reference via the package
|
|
SCOPED, // Between V3Scope and V3Descope: reference via the VarScope
|
|
DESCOPED // After V3Descope: plain reference
|
|
};
|
|
// Hash and equality of constant entries in 'm_consts'
|
|
struct ConstHash final {
|
|
size_t operator()(const AstVar* varp) const {
|
|
return (*this)(VN_AS(varp->valuep(), Const));
|
|
}
|
|
size_t operator()(const AstConst* initp) const { return initp->num().toHash().value(); }
|
|
};
|
|
struct ConstEqual final {
|
|
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
|
|
return (*this)(ap, VN_AS(bp->valuep(), Const)); // Only on collision while rebuild
|
|
} // LCOV_EXCL_STOP
|
|
bool operator()(const AstVar* varp, const AstConst* initp) const {
|
|
// Compare by value, which also checks the width. The dtype is ignored.
|
|
return VN_AS(varp->valuep(), Const)->num().isCaseEq(initp->num());
|
|
}
|
|
};
|
|
// Hash and equality of associative array entries in 'm_maps'
|
|
struct MapHash final {
|
|
size_t operator()(const AstVar* varp) const {
|
|
return (*this)(VN_AS(varp->valuep(), InitArray));
|
|
}
|
|
size_t operator()(const AstInitArray* initp) const {
|
|
return V3Hasher::uncachedHash(initp).value();
|
|
}
|
|
};
|
|
struct MapEqual final {
|
|
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
|
|
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
|
|
} // LCOV_EXCL_STOP
|
|
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
|
|
return equalMap(VN_AS(varp->valuep(), InitArray), initp);
|
|
}
|
|
};
|
|
// Hash and equality of unpacked array entries in 'm_tables'
|
|
struct TableHash final {
|
|
size_t operator()(const AstVar* varp) const {
|
|
return (*this)(VN_AS(varp->valuep(), InitArray));
|
|
}
|
|
size_t operator()(const AstInitArray* initp) const { //
|
|
return hashTable(initp);
|
|
}
|
|
};
|
|
struct TableEqual final {
|
|
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
|
|
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
|
|
} // LCOV_EXCL_STOP
|
|
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
|
|
return equalTable(VN_AS(varp->valuep(), InitArray), initp);
|
|
}
|
|
};
|
|
|
|
// MEMBERS
|
|
Stage m_stage = Stage::UNSCOPED; // Scoping stage of the design, see setScoped()/setDescoped()
|
|
bool m_cacheValid = false; // Cache below is up to date
|
|
AstScope* m_scopep = nullptr; // Scope of the constant pool, between V3Scope and V3Descope
|
|
V3HashSet<AstVar*, ConstHash, ConstEqual> m_consts; // Packed constants
|
|
V3HashSet<AstVar*, MapHash, MapEqual> m_maps; // Associative array constants (maps)
|
|
V3HashSet<AstVar*, TableHash, TableEqual> m_tables; // Unpacked array constants (tables)
|
|
V3HashMap<const AstVar*, AstVarScope*> m_varScopes; // VarScope of each entry iff m_scopep
|
|
uint32_t m_nextConst = 0; // Sequence number for naming
|
|
uint32_t m_nextMap = 0; // Sequence number for naming
|
|
uint32_t m_nextTable = 0; // Sequence number for naming
|
|
|
|
// METHODS
|
|
static V3ConstPool& instance() { return *v3Global.constPoolp(); }
|
|
static bool equalMap(const AstInitArray* ap, const AstInitArray* bp);
|
|
static bool equalTable(const AstInitArray* ap, const AstInitArray* bp);
|
|
static size_t hashTable(const AstInitArray* initp);
|
|
// Find an entry equal to the given initializer in the given set, or create one named from
|
|
// the given prefix and sequence number, and return a read reference to it
|
|
template <typename T_Set, typename T_Init>
|
|
AstVarRef* findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp, T_Init* initp);
|
|
|
|
// CONSTRUCTORS - only V3Global creates and deletes the instance
|
|
V3ConstPool() = default;
|
|
~V3ConstPool() = default;
|
|
VL_UNCOPYABLE(V3ConstPool);
|
|
VL_UNMOVABLE(V3ConstPool);
|
|
|
|
public:
|
|
// STATIC METHODS
|
|
// Find a constant packed variable with the given value, or create one if one does not
|
|
// already exist, and return a read reference to it.
|
|
// The reference has the same dtype as initp.
|
|
// The referenced variable *might* have a different, but compatible dtype.
|
|
static AstVarRef* findConst(AstConst* initp);
|
|
// Find a constant associative array (map) with the given value, or create one if one does
|
|
// not already exist, and return a read reference to it.
|
|
// The reference has the same dtype as initp.
|
|
// The referenced variable *might* have a different, but compatible dtype.
|
|
static AstVarRef* findMap(AstInitArray* initp);
|
|
// Find a constant unpacked array (table) with the given value, or create one if one does
|
|
// not already exist, and return a read reference to it.
|
|
// The reference has the same dtype as initp.
|
|
// The referenced variable *might* have a different, but compatible dtype.
|
|
static AstVarRef* findTable(AstInitArray* initp);
|
|
// Dispatch to the above based on the kind of initializer.
|
|
static AstVarRef* find(AstNodeExpr* initp);
|
|
// Invalidate the lookup cache. This must be called after deleting any Vars from the pool.
|
|
static void invalidateCache() { instance().m_cacheValid = false; }
|
|
// Notify that the design has been scoped, called by V3Scope
|
|
static void setScoped() {
|
|
V3ConstPool& self = instance();
|
|
UASSERT(self.m_stage == Stage::UNSCOPED, "Constant pool scoped twice");
|
|
self.m_stage = Stage::SCOPED;
|
|
invalidateCache();
|
|
}
|
|
// Notify that the design has been descoped, called by V3Descope
|
|
static void setDescoped() {
|
|
V3ConstPool& self = instance();
|
|
UASSERT(self.m_stage == Stage::SCOPED, "Constant pool descoped when not scoped");
|
|
self.m_stage = Stage::DESCOPED;
|
|
invalidateCache();
|
|
}
|
|
// Check the lookup cache refers only to existing nodes, for V3Broken
|
|
static const char* broken();
|
|
};
|
|
|
|
#endif // Guard
|