diff --git a/Changes b/Changes index b38a7d12e..3b7e34195 100644 --- a/Changes +++ b/Changes @@ -7,23 +7,25 @@ indicates the contributor was also the author of the fix; Thanks! *** Support "#delay ;" with associated STMTDLY warning. -**** Fix divide-by-zero errors in constant propagator. [Rodney Sinclair] +**** Fixed divide-by-zero errors in constant propagator. [Rodney Sinclair] -**** Fix wrong result with obscure signed-shift underneath a "? :". +**** Fixed wrong result with obscure signed-shift underneath a "? :". + +**** Fixed many internal memory leaks, and added leak detector. * Verilator 3.654 10/18/2007 **** Don't exit early if many warnings but no errors are found. [Stan Mayer] -**** Fix parsing module #(parameter x,y) declarations. [Oleg Rodionov] +**** Fixed parsing module #(parameter x,y) declarations. [Oleg Rodionov] -**** Fix parsing system functions with empty parens. [Oleg Rodionov] +**** Fixed parsing system functions with empty parens. [Oleg Rodionov] * Verilator 3.653 8/1/2007 **** Support SystemVerilog ==? and !=? operators. -**** Fix SC_LIBS missing from generated makefiles. [Ding Xiaoliang] +**** Fixed SC_LIBS missing from generated makefiles. [Ding Xiaoliang] * Verilator 3.652 6/21/2007 @@ -35,7 +37,7 @@ indicates the contributor was also the author of the fix; Thanks! **** Optimize constant $display arguments. -**** Fix Preprocessor dropping some `line directives. [Mark Nodine] +**** Fixed Preprocessor dropping some `line directives. [Mark Nodine] * Verilator 3.651 5/22/2007 @@ -70,11 +72,11 @@ indicates the contributor was also the author of the fix; Thanks! *** Add USER_CPPFLAGS and USER_LDFLAGS to Makefiles. [Gerald Williams] -**** Fix compile errors under Windows MINGW compiler. [Gerald Williams] +**** Fixed compile errors under Windows MINGW compiler. [Gerald Williams] -**** Fix dotted bit reference to local memory. [Eugene Weber] +**** Fixed dotted bit reference to local memory. [Eugene Weber] -**** Fix 3.640 `verilog forcing IEEE 1364-1995 only. [David Hewson] +**** Fixed 3.640 `verilog forcing IEEE 1364-1995 only. [David Hewson] * Verilator 3.640 3/12/2007 @@ -86,13 +88,13 @@ indicates the contributor was also the author of the fix; Thanks! **** Try all +libext's in the exact order given. [Michael Shinkarovsky] -**** Fix elimination of public signals assigned to constants. [Eugene Weber] +**** Fixed elimination of public signals assigned to constants. [Eugene Weber] -**** Fix internal error when public for loop has empty body. [David Addison] +**** Fixed internal error when public for loop has empty body. [David Addison] -**** Fix "Loops detected" assertion when model exceeds 4GB. [David Hewson] +**** Fixed "Loops detected" assertion when model exceeds 4GB. [David Hewson] -**** Fix display %m names inside named blocks. +**** Fixed display %m names inside named blocks. * Verilator 3.633 2/7/2007 @@ -100,11 +102,11 @@ indicates the contributor was also the author of the fix; Thanks! *** With VL_DEBUG, show wires causing convergence errors. [Mike Shinkarovsky] -**** Fix isolate_assignments when many signals per always. [Mike Shinkarovsky] +**** Fixed isolate_assignments when many signals per always. [Mike Shinkarovsky] -**** Fix isolate_assignments across task/func temporaries. [Mike Shinkarovsky] +**** Fixed isolate_assignments across task/func temporaries. [Mike Shinkarovsky] -**** Fix $display's with array select followed by wide AND. [David Hewson] +**** Fixed $display's with array select followed by wide AND. [David Hewson] * Verilator 3.632 1/17/2007 @@ -115,11 +117,11 @@ indicates the contributor was also the author of the fix; Thanks! ** Support standard NAME[#] for cells created by arraying or generate for. This replaces the non-standard name__# syntax used in earlier versions. -**** Fix again dotted references into generate cells. [David Hewson] +**** Fixed again dotted references into generate cells. [David Hewson] Verilator no longer accepts duplicated variables inside unique generate blocks as this is illegal according to the specification. -**** Fix $readmem* with filenames < 8 characters. [Emerson Suguimoto] +**** Fixed $readmem* with filenames < 8 characters. [Emerson Suguimoto] * Verilator 3.630 12/19/2006 @@ -131,21 +133,21 @@ indicates the contributor was also the author of the fix; Thanks! *** Reduce depth of priority encoded case statements. [Eugene Weber] -**** Fix dotted references inside generated cells. [David Hewson] +**** Fixed dotted references inside generated cells. [David Hewson] -**** Fix missed split optimization points underneath other re-split blocks. +**** Fixed missed split optimization points underneath other re-split blocks. * Verilator 3.623 12/05/2006 *** Add --output-split-cfuncs for accelerating GCC compile. [Eugene Weber] -**** Fix $signed mis-extending when input has a WIDTH violation. [Eugene Weber] +**** Fixed $signed mis-extending when input has a WIDTH violation. [Eugene Weber] **** Add M32 make variable to support -m32 compiles. [Eugene Weber] * Verilator 3.622 10/17/2006 Stable -**** Fix --skip-identical without --debug, broken in 3.621. [Andy Meier] +**** Fixed --skip-identical without --debug, broken in 3.621. [Andy Meier] * Verilator 3.621 10/11/2006 Beta @@ -155,11 +157,11 @@ indicates the contributor was also the author of the fix; Thanks! **** Remove .vpp intermediate files when not under --debug. -**** Fix link error when using --exe with --trace. [Eugene Weber] +**** Fixed link error when using --exe with --trace. [Eugene Weber] -**** Fix mis-optimization of wide concats with constants. +**** Fixed mis-optimization of wide concats with constants. -**** Fix core dump on printing error when not under --debug. [Allan Cochrane] +**** Fixed core dump on printing error when not under --debug. [Allan Cochrane] * Verilator 3.620 10/04/2006 Stable @@ -174,9 +176,9 @@ indicates the contributor was also the author of the fix; Thanks! **** Optimize additional boolean identities (a|a = a, etc.) -**** Fix coredump when dotted cross-ref inside task call. [Eugene Weber] +**** Fixed coredump when dotted cross-ref inside task call. [Eugene Weber] -**** Fix dotted variables in always sensitivity lists. [Allan Cochrane] +**** Fixed dotted variables in always sensitivity lists. [Allan Cochrane] * Verilator 3.610 09/20/2006 Stable @@ -186,13 +188,13 @@ indicates the contributor was also the author of the fix; Thanks! **** Removed coverage request variable; see Coverage limitations in docs. -**** Fix DOS carriage returns in multiline defines. [Ralf Karge] +**** Fixed DOS carriage returns in multiline defines. [Ralf Karge] -**** Fix printf format warnings on 64-bit linux. +**** Fixed printf format warnings on 64-bit linux. * Verilator 3.602 09/11/2006 Stable -**** Fix function references under top inlined module. [David Hewson] +**** Fixed function references under top inlined module. [David Hewson] * Verilator 3.601 09/06/2006 Beta @@ -205,15 +207,15 @@ indicates the contributor was also the author of the fix; Thanks! *** Changed how internal functions are invoked to reduce aliasing. Useful when using GCC's -O2 or -fstrict-aliasing, to gain another ~4%. -**** Fix memory leak when destroying modules. [John Stroebel] +**** Fixed memory leak when destroying modules. [John Stroebel] -**** Fix coredump when unused modules have unused cells. [David Hewson] +**** Fixed coredump when unused modules have unused cells. [David Hewson] -**** Fix 3.600 internal error with arrayed instances. [David Hewson] +**** Fixed 3.600 internal error with arrayed instances. [David Hewson] -**** Fix 3.600 internal error with non-unrolled function loops. [David Hewson] +**** Fixed 3.600 internal error with non-unrolled function loops. [David Hewson] -**** Fix $display %m name not matching Verilog name inside SystemC modules. +**** Fixed $display %m name not matching Verilog name inside SystemC modules. **** Declare optimized lookup tables as 'static', to reduce D-Cache miss rate. @@ -223,17 +225,17 @@ indicates the contributor was also the author of the fix; Thanks! **** Lint for x's in generate case statements. -**** Fix line numbers being off by one when first file starts with newline. +**** Fixed line numbers being off by one when first file starts with newline. -**** Fix naming of generate for blocks to prevent non-inline name conflict. +**** Fixed naming of generate for blocks to prevent non-inline name conflict. -**** Fix redundant statements remaining after table optimization. +**** Fixed redundant statements remaining after table optimization. * Verilator 3.542 08/11/2006 Stable -**** Fix extraneous UNSIGNED warning when comparing genvars. [David Hewson] +**** Fixed extraneous UNSIGNED warning when comparing genvars. [David Hewson] -**** Fix extra white space in $display %c. [by David Addison] +**** Fixed extra white space in $display %c. [by David Addison] **** vl_finish and vl_fatal now print via VL_PRINTF rather then cerr/cout. @@ -241,15 +243,15 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.541 07/05/2006 Beta -*** Fix "// verilator lint_on" not re-enabling warnings. [David Hewson] +*** Fixed "// verilator lint_on" not re-enabling warnings. [David Hewson] -*** Fix 3.540's multiple memory assignments to same block. [David Hewson] +*** Fixed 3.540's multiple memory assignments to same block. [David Hewson] **** Add warning on changeDetect to arrayed structures. [David Hewson] -**** Fix non-zero start number for arrayed instantiations. [Jae Hossell] +**** Fixed non-zero start number for arrayed instantiations. [Jae Hossell] -**** Fix GCC 4.0 header file warnings. +**** Fixed GCC 4.0 header file warnings. * Verilator 3.540 06/27/2006 Beta @@ -257,27 +259,27 @@ indicates the contributor was also the author of the fix; Thanks! **** Optimize delayed assignments to memories inside loops, ~0-5% faster. -**** Fix mis-width warning on bit selects of memories. [David Hewson] +**** Fixed mis-width warning on bit selects of memories. [David Hewson] -**** Fix mis-width warning on dead generate-if branches. [Jae Hossell] +**** Fixed mis-width warning on dead generate-if branches. [Jae Hossell] * Verilator 3.533 06/05/2006 Stable *** Add PDF user manual, verilator.pdf. -**** Fix delayed bit-selected arrayed assignments. [David Hewson] +**** Fixed delayed bit-selected arrayed assignments. [David Hewson] -**** Fix execution path to Perl. [Shanshan Xu] +**** Fixed execution path to Perl. [Shanshan Xu] -**** Fix Bison compile errors in verilog.y. [by Ben Jackson] +**** Fixed Bison compile errors in verilog.y. [by Ben Jackson] * Verilator 3.531 05/10/2006 Stable *** Support $c routines which return 64 bit values. -**** Fix `include `DEFINE. +**** Fixed `include `DEFINE. -**** Fix Verilator core dump when have empty public function. [David.Hewson] +**** Fixed Verilator core dump when have empty public function. [David.Hewson] * Verilator 3.530 04/24/2006 Stable @@ -286,11 +288,11 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.523 03/06/2006 Stable -**** Fix error line numbers being off due to multi-line defines. [Mat Zeno] +**** Fixed error line numbers being off due to multi-line defines. [Mat Zeno] -**** Fix GCC sign extending (uint64_t)(adumpTree(cout,"-treeChange: "); @@ -586,11 +587,29 @@ void AstNode::deleteTree() { // unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this. if (!this) return; UASSERT(m_backp==NULL,"Delete called on node with backlink still set\n"); - this->debugTreeChange("-delete: ", __LINE__, true); + this->debugTreeChange("-delTree: ", __LINE__, true); // MUST be depth first! deleteTreeIter(); } +//====================================================================== +// Memory checks + +#ifdef VL_LEAK_CHECKS +void* AstNode::operator new(size_t size) { + AstNode* objp = static_cast(::operator new(size)); + V3Broken::addNewed(objp); + return objp; +} + +void AstNode::operator delete(void* objp, size_t size) { + if (!objp) return; + AstNode* nodep = static_cast(objp); + V3Broken::deleted(nodep); + ::operator delete(objp); +} +#endif + //====================================================================== // Iterators diff --git a/src/V3Ast.h b/src/V3Ast.h index e608727c2..6a9fd16ce 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -538,6 +538,10 @@ public: // CONSTRUCTORS virtual ~AstNode(); +#ifdef VL_LEAK_CHECKS + static void* operator new(size_t size); + static void operator delete(void* obj, size_t size); +#endif // CONSTANT ACCESSORS static int instrCountBranch() { return 4; } ///< Instruction cycles to branch diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index ee9d554d6..b7344c3fe 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "V3Global.h" #include "V3Broken.h" @@ -45,19 +45,100 @@ class BrokenTable : public AstNVisitor { private: // MEMBERS // For each node, we keep if it exists or not. - typedef set NodeSet; - static NodeSet s_nodes; // Set of all nodes that exist + typedef map NodeMap; + static NodeMap s_nodes; // Set of all nodes that exist + // BITMASK + static const int FLAG_ALLOCATED = 0x01; // new() and not delete()ed + static const int FLAG_IN_TREE = 0x02; // Is in netlist tree + static const int FLAG_LINKABLE = 0x04; // Is in netlist tree, can be linked to + static const int FLAG_LEAKED = 0x08; // Known to have been leaked public: // METHODS - static void add(const AstNode* nodep) { - s_nodes.insert(nodep); + static void deleted(const AstNode* nodep) { + // Called by operator delete on any node - only if VL_LEAK_CHECKS + if (debug()) cout<<"-nodeDel: "<<(void*)(nodep)<second & FLAG_ALLOCATED)) { + ((AstNode*)(nodep))->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted\n"); + } + if (iter!=s_nodes.end()) s_nodes.erase(iter); } - static bool exists(const AstNode* nodep) { - NodeSet::iterator iter = s_nodes.find(nodep); - return (iter != s_nodes.end()); + static void addNewed(const AstNode* nodep) { + // Called by operator new on any node - only if VL_LEAK_CHECKS + if (debug()) cout<<"-nodeNew: "<<(void*)(nodep)<second & FLAG_ALLOCATED)) { + ((AstNode*)(nodep))->v3fatalSrc("Newing AstNode object that is already allocated\n"); + } + if (iter == s_nodes.end()) { + s_nodes.insert(make_pair(nodep,FLAG_ALLOCATED)); + } } - static void clear() { + static void addInTree(AstNode* nodep, bool linkable) { +#ifndef VL_LEAK_CHECKS + if (!linkable) return; // save some time, else the map will get huge! +#endif + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter == s_nodes.end()) { +#ifdef VL_LEAK_CHECKS + nodep->v3fatalSrc("AstNode is in tree, but not allocated\n"); +#endif + } else { + if (!(iter->second & FLAG_ALLOCATED)) { +#ifdef VL_LEAK_CHECKS + nodep->v3fatalSrc("AstNode is in tree, but not allocated\n"); +#endif + } + if (iter->second & FLAG_IN_TREE) { + nodep->v3fatalSrc("AstNode is already in tree at another location\n"); + } + } + int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0); + if (iter == s_nodes.end()) { + s_nodes.insert(make_pair(nodep,or_flags)); + } else { + iter->second |= or_flags; + } + } + static bool okIfLinkedTo(const AstNode* nodep) { + // Someone has a pointer to this node. Is it kosher? + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter == s_nodes.end()) return false; +#ifdef VL_LEAK_CHECKS + if (!(iter->second & FLAG_ALLOCATED)) return false; +#endif + if (!(iter->second & FLAG_IN_TREE)) return false; + if (!(iter->second & FLAG_LINKABLE)) return false; + return true; + } + static void prepForTree() { +#ifndef VL_LEAK_CHECKS s_nodes.clear(); +#endif + for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { + it->second &= ~FLAG_IN_TREE; + it->second &= ~FLAG_LINKABLE; + } + } + static void doneWithTree() { + for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without + for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { + if ((it->second & FLAG_ALLOCATED) + && !(it->second & FLAG_IN_TREE) + && !(it->second & FLAG_LEAKED) + && (it->first->backp() ? backs==1 : backs==0)) { + // Use only AstNode::dump instead of the virtual one, as there + // may be varp() and other cross links that are bad. + if (debug()) { + cout<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": "); + ((AstNode*)(it->first))->AstNode::dump(cout); + cout<second |= FLAG_LEAKED; + } + } + } } public: // CONSTUCTORS @@ -65,11 +146,11 @@ public: virtual ~BrokenTable() {} }; -BrokenTable::NodeSet BrokenTable::s_nodes; +BrokenTable::NodeMap BrokenTable::s_nodes; bool AstNode::brokeExists() const { // Called by node->broken() routines to do table lookup - return BrokenTable::exists(this); + return BrokenTable::okIfLinkedTo(this); } //###################################################################### @@ -82,9 +163,7 @@ private: // // so userp and friends may not be used // VISITORS virtual void visit(AstNode* nodep, AstNUser*) { - if (nodep->maybePointedTo()) { - BrokenTable::add(nodep); - } + BrokenTable::addInTree(nodep, nodep->maybePointedTo()); nodep->iterateChildren(*this); } public: @@ -127,8 +206,15 @@ public: void V3Broken::brokenAll(AstNetlist* nodep) { //UINFO(9,__FUNCTION__<<": "<deleteTree(); tree1p=NULL; + return tree0p; + } // Must have differing logic, so make a selection @@ -245,8 +248,7 @@ private: // CASEx(cexpr,.... // -> tree of IF(msb, IF(msb-1, 11, 10) // IF(msb-1, 01, 00)) - AstNode* cexprp = nodep->exprp(); - cexprp->unlinkFrBack(); + AstNode* cexprp = nodep->exprp()->unlinkFrBack(); if (debug()>=9) { for (uint32_t i=0; i<(1UL<replaceWith(ifrootp); else nodep->unlinkFrBack(); + nodep->deleteTree(); nodep=NULL; + cexprp->deleteTree(); cexprp=NULL; if (debug()>=9) ifrootp->dumpTree(cout," _simp: "); } @@ -304,6 +308,7 @@ private: and2p = new AstAnd(itemp->fileline(), new AstConst(itemp->fileline(), numval), new AstConst(itemp->fileline(), nummask)); + icondp->deleteTree(); icondp=NULL; iconstp=NULL; } else { // Not a caseX mask, we can simply build CASEEQ(cexpr icond) and1p = cexprp->cloneTree(false); @@ -320,6 +325,7 @@ private: itemp->condsp(ifexprp); } } + cexprp->deleteTree(); cexprp=NULL; if (!hadDefault) { // If there was no default, add a empty one, this greatly simplifies below code // and constant propagation will just eliminate it for us later. diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 94d4848d3..425991c74 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -384,6 +384,8 @@ private: int shift1 = shift1p->castConst()->asInt(); if (lhsp->castShiftR()) shift1=-shift1; int shift2 = shift2p->castConst()->asInt(); if (nodep->castShiftR()) shift2=-shift2; int newshift = shift1+shift2; + shift1p->deleteTree(); shift1p=NULL; + shift2p->deleteTree(); shift1p=NULL; AstNode* newp; V3Number mask1 (nodep->fileline(), nodep->width()); V3Number ones (nodep->fileline(), nodep->width()); @@ -524,7 +526,8 @@ private: if (debug()>=9) asn1p->dumpTree(cout," _new: "); if (debug()>=9) asn2p->dumpTree(cout," _new: "); // Cleanup - nodep->unlinkFrBack()->deleteTree(); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + conp->deleteTree(); conp=NULL; // Further reduce, either node may have more reductions. return true; } @@ -637,6 +640,7 @@ private: // If bp was a concat, then we have this exact same form again! // Recurse rather then calling node->iterate to prevent 2^n recursion! if (operandConcatMove(abConcp)) moveConcat(abConcp); + bcConcp->deleteTree(); bcConcp=NULL; } else { AstConcat* abConcp = nodep->lhsp()->castConcat(); abConcp->unlinkFrBack(); AstNode* ap = abConcp->lhsp()->unlinkFrBack(); @@ -647,6 +651,7 @@ private: nodep->lhsp(ap); nodep->rhsp(bcConcp); if (operandConcatMove(bcConcp)) moveConcat(bcConcp); + abConcp->deleteTree(); abConcp=NULL; } } @@ -677,6 +682,8 @@ private: if (lsb1p->castConst() && lsb2p->castConst()) { newlsbp = new AstConst(lsb1p->fileline(), lsb1p->castConst()->asInt() + lsb2p->castConst()->asInt()); + lsb1p->deleteTree(); lsb1p=NULL; + lsb2p->deleteTree(); lsb2p=NULL; } else { // Width is important, we need the width of the fromp's // expression, not the potentially smaller lsb1p's width @@ -860,7 +867,7 @@ private: new AstAssign(nodep->fileline(), varrefp, exprp)); m_modp->addStmtp(newinitp); - nodep->unlinkFrBack(); nodep=NULL; + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Set the initial value right in the variable so we can constant propagate AstNode* initvaluep = exprp->cloneTree(false); varrefp->varp()->initp(initvaluep); diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index c4066693c..40e924e21 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -110,18 +110,6 @@ private: } AstVarScope* createVarSc(AstVarScope* oldvarscp, string name, int width/*0==fromoldvar*/) { // Because we've already scoped it, we may need to add both the AstVar and the AstVarScope - AstRange* rangep = NULL; - if (width==0) { - rangep = new AstRange(oldvarscp->fileline(), - oldvarscp->varp()->msb(), - oldvarscp->varp()->lsb()); - } else if (width==1) { - rangep = NULL; - } else { - rangep = new AstRange(oldvarscp->fileline(), - width-1, 0); - } - if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped"); AstVar* varp; AstModule* addmodp = oldvarscp->scopep()->modp(); @@ -131,6 +119,17 @@ private: // Created module's AstVar earlier under some other scope varp = iter->second; } else { + AstRange* rangep = NULL; + if (width==0) { + rangep = new AstRange(oldvarscp->fileline(), + oldvarscp->varp()->msb(), + oldvarscp->varp()->lsb()); + } else if (width==1) { + rangep = NULL; + } else { + rangep = new AstRange(oldvarscp->fileline(), + width-1, 0); + } varp = new AstVar (oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, rangep); if (width==0) varp->widthSignedFrom(oldvarscp); addmodp->addStmtp(varp); diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 556fa5d08..702882617 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "V3Error.h" #ifndef _V3ERROR_NO_GLOBAL_ # include "V3Ast.h" @@ -170,6 +171,46 @@ void FileLine::v3errorEnd(ostringstream& str) { V3Error::v3errorEnd(str); } } + +#ifdef VL_LEAK_CHECKS +typedef set FileLineCheckSet; +FileLineCheckSet fileLineLeakChecks; + +void* FileLine::operator new(size_t size) { + FileLine* objp = static_cast(::operator new(size)); + fileLineLeakChecks.insert(objp); + return objp; +} + +void FileLine::operator delete(void* objp, size_t size) { + if (!objp) return; + FileLine* flp = static_cast(objp); + FileLineCheckSet::iterator it = fileLineLeakChecks.find(flp); + if (it != fileLineLeakChecks.end()) { + fileLineLeakChecks.erase(it); + } else { + flp->v3fatalSrc("Deleting FileLine object that was never tracked\n"); + } + ::operator delete(objp); +} +#endif + +void FileLine::deleteAllRemaining() { +#ifdef VL_LEAK_CHECKS + // FileLines are allocated, but never nicely freed, as it's much faster + // that way. Unfortunately this makes our leak checking a big mess, so + // only when leak checking we'll track them all and cleanup. + while (1) { + FileLineCheckSet::iterator it=fileLineLeakChecks.begin(); + if (it==fileLineLeakChecks.end()) break; + delete *it; + // Operator delete will remove the iterated object from the list. + // Eventually the list will be empty and terminate the loop. + } + fileLineLeakChecks.clear(); +#endif +} + //###################################################################### // V3Error class functions diff --git a/src/V3Error.h b/src/V3Error.h index a9d1415c6..449a3df0d 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -117,8 +117,6 @@ class V3Error { static ostringstream s_errorStr; // Error string being formed static V3ErrorCode s_errorCode; // Error string being formed will abort enum MaxErrors { MAX_ERRORS = 50 }; // Fatal after this may errors - static void incErrors(); - static void incWarnings(); V3Error() { cerr<<("Static class"); abort(); } @@ -132,6 +130,8 @@ class V3Error { static int warnCount() { return s_warnCount; } static int errorOrWarnCount() { return errorCount()+warnCount(); } // METHODS + static void incErrors(); + static void incWarnings(); static void init(); static void abortIfErrors(); static void abortIfWarnings(); @@ -199,9 +199,14 @@ protected: void incLineno() { m_lineno++; } FileLine* copyOrSameFileLine(); public: - FileLine (const string& filename, int lineno) { m_lineno=lineno; m_filename = filename; m_warnOff=s_defaultFileLine.m_warnOff;} - FileLine (FileLine* fromp) { m_lineno=fromp->lineno(); m_filename = fromp->filename(); m_warnOff=fromp->m_warnOff;}; - FileLine (EmptySecret) { m_lineno=0; m_filename="COMMAND_LINE"; m_warnOff=0; } // Only for static constructor + FileLine (const string& filename, int lineno) { m_lineno=lineno; m_filename = filename; m_warnOff=s_defaultFileLine.m_warnOff; } + FileLine (FileLine* fromp) { m_lineno=fromp->lineno(); m_filename = fromp->filename(); m_warnOff=fromp->m_warnOff; } + FileLine (EmptySecret) { m_lineno=0; m_filename="COMMAND_LINE"; m_warnOff=0; } + ~FileLine() { } +#ifdef VL_LEAK_CHECKS + static void* operator new(size_t size); + static void operator delete(void* obj, size_t size); +#endif static FileLine& defaultFileLine() { return s_defaultFileLine; } int lineno () const { return m_lineno; } string ascii() const; @@ -218,6 +223,8 @@ public: void v3errorEnd(ostringstream& str); inline bool operator==(FileLine rhs) { return (m_lineno==rhs.m_lineno && m_filename==rhs.m_filename); } + + static void deleteAllRemaining(); }; ostream& operator<<(ostream& os, FileLine* fileline); diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 16030d940..92bad7b4f 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -531,6 +531,8 @@ private: rhsp, lsb))); } } + rhsp->deleteTree(); rhsp=NULL; + destp->deleteTree(); destp=NULL; } else { UINFO(8," ASSIGNSEL(const,narrow) "<isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep); diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 0d8c430f3..3b6060ed2 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -151,7 +151,7 @@ private: // Remove the cell newmodp->deleteTree(); newmodp=NULL; // Clear any leftover ports, etc nodep->unlinkFrBack(); - nodep = NULL; + pushDeletep(nodep); nodep = NULL; if (debug()>=9) { m_modp->dumpTree(cout,"donemod:"); } } } @@ -332,14 +332,14 @@ private: } else { m_modp->user(1); } - nodep->unlinkFrBack(); nodep=NULL; // Remove so don't propagate to upper cell... + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Remove so don't propagate to upper cell... } else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) { if (!m_modp) { nodep->v3error("Inline pragma not under a module"); } else { cantInline("Pragma NO_INLINE_MODULE"); } - nodep->unlinkFrBack(); nodep=NULL; // Remove so don't propagate to upper cell... + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Remove so don't propagate to upper cell... } else { nodep->iterateChildren(*this); } diff --git a/src/V3Link.cpp b/src/V3Link.cpp index 06111472d..e2e2ba339 100644 --- a/src/V3Link.cpp +++ b/src/V3Link.cpp @@ -165,7 +165,7 @@ private: if ((findvarp->isIO() && nodep->isSignal()) || (findvarp->isSignal() && nodep->isIO())) { findvarp->combineType(nodep); - nodep->unlinkFrBack(); nodep=NULL; + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } else { nodep->v3error("Duplicate declaration of signal: "<prettyName()); findvarp->v3error("... Location of original declaration"); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index a9019cbd6..802a69b29 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -155,6 +155,9 @@ public: void movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp); // Mark one vertex as finished, remove from ready list if done // STATIC MEMBERS (for lookup) static void clear() { + for (DomScopeMap::iterator it=s_dsMap.begin(); it!=s_dsMap.end(); ++it) { + delete it->second; + } s_dsMap.clear(); } V3List& readyVertices() { return m_readyVertices; } @@ -426,15 +429,18 @@ private: m_finder.main(m_topScopep); // ProcessDomainsIterate will use these when it needs to move // something to a combodomain. This saves a ton of find() operations. - AstSenTree comb (nodep->fileline(), // Gets cloned() so ok if goes out of scope - new AstSenItem(nodep->fileline(), AstSenItem::Combo())); - m_comboDomainp = m_finder.getSenTree(nodep->fileline(), &comb); - AstSenTree settle (nodep->fileline(), // Gets cloned() so ok if goes out of scope - new AstSenItem(nodep->fileline(), AstSenItem::Settle())); - m_settleDomainp = m_finder.getSenTree(nodep->fileline(), &settle); + AstSenTree* combp = new AstSenTree (nodep->fileline(), // Gets cloned() so ok if goes out of scope + new AstSenItem(nodep->fileline(), AstSenItem::Combo())); + m_comboDomainp = m_finder.getSenTree(nodep->fileline(), combp); + pushDeletep(combp); // Cleanup when done + AstSenTree* settlep = new AstSenTree (nodep->fileline(), // Gets cloned() so ok if goes out of scope + new AstSenItem(nodep->fileline(), AstSenItem::Settle())); + m_settleDomainp = m_finder.getSenTree(nodep->fileline(), settlep); + pushDeletep(settlep); // Cleanup when done // Fake AstSenTree we set domainp to indicate needs deletion m_deleteDomainp = new AstSenTree (nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle())); + pushDeletep(m_deleteDomainp); // Cleanup when done UINFO(5," DeleteDomain = "<debug(debug()); // Default defines FileLine* prefl = new FileLine("INTERNAL_VERILATOR_DEFINE",0); - s_preprocp->define(prefl,"verilator", "1"); - s_preprocp->define(prefl,"verilator3", "1"); - s_preprocp->define(prefl,"systemc_clock", "/*verilator systemc_clock*/"); - s_preprocp->define(prefl,"coverage_block_off", "/*verilator coverage_block_off*/"); + s_preprocp->define(prefl,"verilator", "1"); // LEAK_OK + s_preprocp->define(prefl,"verilator3", "1"); // LEAK_OK + s_preprocp->define(prefl,"systemc_clock", "/*verilator systemc_clock*/"); // LEAK_OK + s_preprocp->define(prefl,"coverage_block_off", "/*verilator coverage_block_off*/"); // LEAK_OK } } diff --git a/src/V3Read.cpp b/src/V3Read.cpp index ac323090a..16e6009bc 100644 --- a/src/V3Read.cpp +++ b/src/V3Read.cpp @@ -78,6 +78,19 @@ void V3Read::statePop() { s_readp->m_lexerp->statePop(); } //###################################################################### // Read class functions +V3Read::~V3Read() { + for (deque::iterator it = m_stringps.begin(); it != m_stringps.end(); ++it) { + delete (*it); + } + m_stringps.clear(); + for (deque::iterator it = m_numberps.begin(); it != m_numberps.end(); ++it) { + delete (*it); + } + m_numberps.clear(); + if (m_lexerp) { delete m_lexerp; m_lexerp = NULL; } + parserClear(); +} + void V3Read::readFile(FileLine* fileline, const string& modfilename, bool inLibrary) { string modname = V3Options::filenameNonExt(modfilename); diff --git a/src/V3Read.h b/src/V3Read.h index 8e66dce17..a0346c80b 100644 --- a/src/V3Read.h +++ b/src/V3Read.h @@ -110,16 +110,8 @@ public: m_inBeginKwd = 0; m_lastVerilogState = stateVerilogRecent(); } - ~V3Read() { - for (deque::iterator it = m_stringps.begin(); it != m_stringps.end(); ++it) { - delete (*it); - } - m_stringps.clear(); - for (deque::iterator it = m_numberps.begin(); it != m_numberps.end(); ++it) { - delete (*it); - } - m_numberps.clear(); - } + ~V3Read(); + void parserClear(); // METHODS // Preprocess and read the Verilog file specified into the netlist database diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 4409cf4f9..765faa897 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -349,6 +349,7 @@ private: // Connect to this exact variable AstVarScope* localVscp = varrefp->varScopep(); if (!localVscp) varrefp->v3fatalSrc("Null var scope"); portp->user2p(localVscp); + pushDeletep(pinp); } else { pinp->v3warn(TASKNSVAR,"Unsupported: Function/task input argument is not simple variable"); } diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index f2c2810f2..c96ec2766 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -95,6 +95,8 @@ private: || (rhsp->castConst() && rhsp->castConst()->num().isFourState()))) { V3Number num (nodep->fileline(), 1, (nodep->castEqCase()?0:1)); newp = new AstConst (nodep->fileline(), num); + lhsp->deleteTree(); lhsp=NULL; + rhsp->deleteTree(); rhsp=NULL; } else { if (nodep->castEqCase()) newp = new AstEq (nodep->fileline(), lhsp, rhsp); @@ -221,6 +223,7 @@ private: if (debug()>=9) newref1p->dumpTree(cout," _new: "); if (debug()>=9) newvarp->dumpTree(cout," _new: "); if (debug()>=9) newinitp->dumpTree(cout," _new: "); + nodep->deleteTree(); nodep=NULL; } } } diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index 122b32572..a2ba56324 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -198,6 +198,7 @@ private: constInitp->num(), condBip, constStopp->num(), incInstrp, constIncp->num()); nodep = NULL; + // Cleanup return true; } @@ -229,7 +230,6 @@ private: loopValue.opAssign(numInit); AstNode* newbodysp = NULL; - AstNode* clonedIncsp = NULL; // Last cloned incp() statements m_statLoops++; if (stmtsp) { int times = 0; @@ -244,25 +244,8 @@ private: // Replace iterator values with constant. AstNode* oneloopp = stmtsp->cloneTree(true); - // A nicer way to propage the loop constant would be to set the variable to the value - // and call a constant-propagator like V3Table, so temp values - // that are calculated propagate down. - // If we do this, we can remove the below - if (nodep->castWhile() && incp) { - if (clonedIncsp) { - // Previous iteration of loop set the variable. - // This set is redundant with this next iteration and can be removed. - if (clonedIncsp == newbodysp) { // Increment was only thing in list - newbodysp = NULL; - } else { - clonedIncsp->unlinkFrBack(); - } - clonedIncsp->deleteTree(); - } - clonedIncsp = incp->clonep(); if (!clonedIncsp) nodep->v3fatalSrc("inc failed"); - } - m_varValuep = new AstConst(nodep->fileline(), loopValue); + m_varModeReplace = true; oneloopp->iterateAndNext(*this); m_varModeReplace = false; @@ -280,6 +263,8 @@ private: V3Number newnum(nodep->fileline(), m_forVarp->width()); // Can't increment in-place incInstrp->numberOperate(newnum, loopValue, numInc); loopValue.opAssign(newnum); + + pushDeletep(m_varValuep); m_varValuep=NULL; } } } @@ -287,6 +272,9 @@ private: // Replace the FOR() if (newbodysp) nodep->replaceWith(newbodysp); else nodep->unlinkFrBack(); + if (bodysp) { pushDeletep(bodysp); bodysp=NULL; } + if (precondsp) { pushDeletep(precondsp); precondsp=NULL; } + if (initp) { pushDeletep(initp); initp=NULL; } if (debug()>=9) newbodysp->dumpTree(cout,"- _new: "); } @@ -311,7 +299,7 @@ private: if (forUnrollCheck(nodep, initp, nodep->precondsp(), nodep->condp(), incp, nodep->bodysp())) { - nodep=NULL; // Did replacement + pushDeletep(nodep); nodep=NULL; // Did replacement } } } @@ -328,7 +316,7 @@ private: if (forUnrollCheck(nodep, nodep->initsp(), NULL, nodep->condp(), nodep->incsp(), nodep->bodysp())) { - nodep=NULL; // Did replacement + pushDeletep(nodep); nodep=NULL; // Did replacement } else { nodep->v3error("For loop doesn't have genvar index, or is misformed"); } @@ -373,6 +361,7 @@ private: && !nodep->lvalue()) { AstNode* newconstp = m_varValuep->cloneTree(false); nodep->replaceWith(newconstp); + pushDeletep(nodep); } } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5ccd62e3b..645f6bc24 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -760,6 +760,7 @@ void WidthVisitor::fixWidthExtend (AstNode* nodep, int expWidth) { AstNode* newp = new AstConst(nodep->fileline(), num); newp->signedFrom(constp); constp->replaceWith(newp); + pushDeletep(constp); constp=NULL; nodep=newp; } else if (expWidthwidth()) { // Trunc - Extract diff --git a/src/Verilator.cpp b/src/Verilator.cpp index f1d74ec5f..6f21d62dd 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -552,6 +552,7 @@ int main(int argc, char** argv, char** env) { // Cleanup memory for valgrind leak analysis v3Global.clear(); #endif + FileLine::deleteAllRemaining(); UINFO(1,"Done, Exiting...\n"); } diff --git a/src/verilog.y b/src/verilog.y index 587ab68bd..db555d064 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -66,6 +66,10 @@ public: nodep->addNext(new AstStop(fileline)); return nodep; } + static void setRange(AstRange* rangep) { + if (s_varRangep) { s_varRangep->deleteTree(); s_varRangep=NULL; } // It was cloned, so this is safe. + s_varRangep = rangep; + } static string deQuote(FileLine* fileline, string text); }; @@ -88,7 +92,7 @@ AstCase* V3Parse::s_caseAttrp = NULL; #define VARDECL(type) { V3Parse::s_varDecl = AstVarType::type; } #define VARIO(type) { V3Parse::s_varIO = AstVarType::type; } #define VARSIGNED(value) { V3Parse::s_varSigned = value; } -#define VARRANGE(range) { V3Parse::s_varRangep=(range); } +#define VARRANGE(rangep) { V3Parse::setRange(rangep); } #define INSTPREP(modname,paramsp) { V3Parse::s_impliedDecl = true; V3Parse::s_instModule = modname; V3Parse::s_instParamp = paramsp; } @@ -858,8 +862,8 @@ stmt: ';' { $$ = NULL; } | yD_ERROR parenE ';' { $$ = V3Parse::createDisplayError($1); } | yD_ERROR '(' yaSTRING commaEListE ')' ';' { $$ = new AstDisplay($1,AstDisplayType::ERROR, *$3,NULL,$4); $$->addNext(new AstStop($1)); } | yD_FATAL parenE ';' { $$ = new AstDisplay($1,AstDisplayType::FATAL, "", NULL,NULL); $$->addNext(new AstStop($1)); } - | yD_FATAL '(' expr ')' ';' { $$ = new AstDisplay($1,AstDisplayType::FATAL, "", NULL,NULL); $$->addNext(new AstStop($1)); } - | yD_FATAL '(' expr ',' yaSTRING commaEListE ')' ';' { $$ = new AstDisplay($1,AstDisplayType::FATAL, *$5,NULL,$6); $$->addNext(new AstStop($1)); } + | yD_FATAL '(' expr ')' ';' { $$ = new AstDisplay($1,AstDisplayType::FATAL, "", NULL,NULL); $$->addNext(new AstStop($1)); if ($3) $3->deleteTree(); } + | yD_FATAL '(' expr ',' yaSTRING commaEListE ')' ';' { $$ = new AstDisplay($1,AstDisplayType::FATAL, *$5,NULL,$6); $$->addNext(new AstStop($1)); if ($3) $3->deleteTree(); } | yD_READMEMB '(' expr ',' varRefMem ')' ';' { $$ = new AstReadMem($1,false,$3,$5,NULL,NULL); } | yD_READMEMB '(' expr ',' varRefMem ',' expr ')' ';' { $$ = new AstReadMem($1,false,$3,$5,$7,NULL); } @@ -1314,6 +1318,11 @@ pslExpr: exprPsl { $$ = new AstPslBool($1->fileline(), $1); } //********************************************************************** %% +void V3Read::parserClear() { + // Clear up any dynamic memory V3Parser required + V3Parse::setRange(NULL); +} + AstNode* V3Parse::createSupplyExpr(FileLine* fileline, string name, int value) { FileLine* newfl = new FileLine (fileline); newfl->warnOff(V3ErrorCode::WIDTH, true); @@ -1328,6 +1337,7 @@ AstNode* V3Parse::createSupplyExpr(FileLine* fileline, string name, int value) { AstVar* V3Parse::createVariable(FileLine* fileline, string name, AstRange* arrayp) { AstVarType type = V3Parse::s_varIO; AstRange* rangep = V3Parse::s_varRangep; + AstRange* cleanup_rangep = NULL; //UINFO(0,"CREVAR "<ascii()<<" decl="<v3error("Integers may not be ranged: "<v3error("Genvars may not be arrayed: "<deleteTree(); cleanup_rangep=NULL; } return nodep; }