diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 506d8047e..28f15e698 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -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 diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index fc05797e7..064503267 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -23,6 +23,10 @@ #include #include +#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 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(s_nodePool[index]); + s_nodePool[index] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64); // For later new() + return objp; + } else { + AstNode* const objp = static_cast(::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 diff --git a/src/V3Ast.h b/src/V3Ast.h index 1051a4b31..87ed4959f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -560,7 +560,7 @@ public: // Perform a function on every link in a node virtual void foreachLink(std::function 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 diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 6992d0870..28c7d75b8 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -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(); diff --git a/src/V3Options.h b/src/V3Options.h index 6340490b7..138c597b3 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -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 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; } diff --git a/src/V3SymTable.h b/src/V3SymTable.h index 019bfbe19..305d1652f 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -104,7 +104,7 @@ public: m_classOrPackagep = reinterpret_cast(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) {} diff --git a/src/astgen b/src/astgen index 3d26c0474..1865f51f6 100755 --- a/src/astgen +++ b/src/astgen @@ -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(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)