Internals: Add VL_ALLOC_RANDOM_CHECKS

This commit is contained in:
Wilson Snyder 2026-02-28 18:21:51 -05:00
parent 230ce772c2
commit e23c7a510e
7 changed files with 60 additions and 2 deletions

View File

@ -102,6 +102,7 @@ LIBS = $(CFG_LIBS) -lm
CPPFLAGS += -DVERILATOR_INTERNAL_
CPPFLAGS += -MMD
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
#CPPFLAGS += -DVL_ALLOC_RANDOM_CHECKS # To allow --debug-new-random
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGS += -MP # Only works on recent GCC versions
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users

View File

@ -23,6 +23,10 @@
#include <memory>
#include <sstream>
#ifdef VL_ALLOC_RANDOM_CHECKS
#include "V3Ast__gen_sizeof.h"
#endif
VL_DEFINE_DEBUG_FUNCTIONS;
//======================================================================
@ -992,6 +996,43 @@ void AstNode::operator delete(void* objp, size_t size) {
}
#endif
#ifdef VL_ALLOC_RANDOM_CHECKS
void* AstNode::operator new(size_t size) {
// Make the following small to debug this routine, larger for performance and better random
constexpr size_t POOL_SIZE = 65536; // Ideally large enough to fit all nodes used in tests
// Randomly select from a large pool (POOL_SIZE) of max-node sized (MAX_NODE_SIZE) pointers
static uint64_t s_lfsr = 0; // LFSR, 0 = didn't initialize yet
static std::array<void*, POOL_SIZE> s_nodePool;
if (int seed = v3Global.opt.debugAllocRandom()) {
constexpr uint64_t POLYNOMIAL = 0x80000000000019e2ULL;
UASSERT_STATIC(size <= ASTGEN_MAX_NODE_SIZE, "fix ASTGEN_MAX_NODE_SIZE");
if (!s_lfsr) {
s_lfsr = seed;
if (!s_lfsr) s_lfsr = ~s_lfsr;
for (size_t i = 0; i < POOL_SIZE; ++i) {
s_nodePool[i] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64);
}
// Sort, just to make it more obvious we are properly randomizing
std::sort(std::begin(s_nodePool), std::end(s_nodePool));
}
// Xoroshiro128+ algorithm
s_lfsr = (s_lfsr & 1ULL) ? ((s_lfsr >> 1ULL) ^ POLYNOMIAL) : (s_lfsr >> 1ULL);
const size_t index = s_lfsr % POOL_SIZE;
AstNode* const objp = static_cast<AstNode*>(s_nodePool[index]);
s_nodePool[index] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64); // For later new()
return objp;
} else {
AstNode* const objp = static_cast<AstNode*>(::operator new(size));
return objp;
}
}
void AstNode::operator delete(void* objp, size_t size) {
// Leak due to size difference between true node and MAX_NODE_SIZE
(void)objp;
(void)size;
}
#endif
//======================================================================
// Iterators

View File

@ -560,7 +560,7 @@ public:
// Perform a function on every link in a node
virtual void foreachLink(std::function<void(AstNode** linkpp, const char* namep)> f) = 0;
#ifdef VL_LEAK_CHECKS
#if defined(VL_LEAK_CHECKS) || defined(VL_ALLOC_RANDOM_CHECKS)
static void* operator new(size_t size);
static void operator delete(void* obj, size_t size);
#endif

View File

@ -1375,6 +1375,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-debug-abort", CbCall, []() {
V3Error::vlAbort(); // LCOV_EXCL_LINE
}).undocumented(); // See also --debug-sigseg
#ifdef VL_ALLOC_RANDOM_CHECKS
DECL_OPTION("-debug-alloc-random", Set, &m_debugAllocRandom).undocumented();
#endif
DECL_OPTION("-debug-check", OnOff, &m_debugCheck);
DECL_OPTION("-debug-collision", OnOff, &m_debugCollision).undocumented();
DECL_OPTION("-debug-emitv", OnOff, &m_debugEmitV).undocumented();

View File

@ -315,6 +315,7 @@ private:
int m_coverageExprMax = 32; // main switch: --coverage-expr-max
int m_convergeLimit = 100; // main switch: --converge-limit
int m_coverageMaxWidth = 256; // main switch: --coverage-max-width
int m_debugAllocRandom = 0; // main switch: --debug-alloc-random <seed>
int m_expandLimit = 256; // main switch: --expand-limit
int m_gateStmts = 100; // main switch: --gate-stmts
int m_hierChild = 0; // main switch: --hierarchical-child
@ -597,6 +598,7 @@ public:
int convergeLimit() const { return m_convergeLimit; }
int coverageExprMax() const { return m_coverageExprMax; }
int coverageMaxWidth() const { return m_coverageMaxWidth; }
int debugAllocRandom() const { return m_debugAllocRandom; }
bool dumpTreeAddrids() const VL_MT_SAFE;
int expandLimit() const { return m_expandLimit; }
int gateStmts() const { return m_gateStmts; }

View File

@ -104,7 +104,7 @@ public:
m_classOrPackagep = reinterpret_cast<AstNodeModule*>(1);
#endif
}
#if defined(VL_DEBUG) && !defined(VL_LEAK_CHECKS)
#if (defined(VL_DEBUG) || defined(VL_ALLOC_RANDOM_CHECKS)) && !defined(VL_LEAK_CHECKS)
// For testing, leak so above destructor 1 assignments work
void* operator new(size_t size) { return std::malloc(size); }
void operator delete(void* objp, size_t size) {}

View File

@ -935,6 +935,16 @@ def write_ast_type_info(filename):
))
def write_ast_sizeof(filename):
with open_file(filename) as fh:
fh.write("static constexpr size_t ASTGEN_MAX_NODE_SIZE =\n")
paren = ""
for node in AstNodeList:
fh.write(" std::max(sizeof(Ast{n}),\n".format(n=node.name))
paren += ")"
fh.write(" static_cast<size_t>(0)" + paren + ";\n")
def write_ast_impl(filename):
with open_file(filename) as fh:
@ -1584,6 +1594,7 @@ if Args.classes:
write_ast_impl("V3Ast__gen_impl.h")
write_ast_macros("V3Ast__gen_macros.h")
write_ast_yystype("V3Ast__gen_yystype.h")
write_ast_sizeof("V3Ast__gen_sizeof.h")
# Write Dfg code
write_forward_class_decls("Dfg", DfgVertexList)
write_visitor_decls("Dfg", DfgVertexList)