Fix random internal crashes (#666).

Internally, in V3Broken check m_interpp for null, and make several cleanups
in the addNext/addNextHere/unlinkFr* methods.
This commit is contained in:
Wilson Snyder 2023-03-18 09:47:46 -04:00
parent 70c2d1eded
commit d60a3e2b66
5 changed files with 41 additions and 17 deletions

View File

@ -23,6 +23,7 @@ Verilator 5.009 devel
* Fix push to dynamic queue in struct (#4015). [ezchi] * Fix push to dynamic queue in struct (#4015). [ezchi]
* Fix large return blocks with --comp-limit-blocks (#4028). [tenghtt] * Fix large return blocks with --comp-limit-blocks (#4028). [tenghtt]
* Fix clocking block scope internal error (#4032). [Srinivasan Venkataramanan] * Fix clocking block scope internal error (#4032). [Srinivasan Venkataramanan]
* Fix random internal crashes (#666). [Dag Lem]
* Fix false ENUMVALUE on expressions and arrays. * Fix false ENUMVALUE on expressions and arrays.

View File

@ -298,17 +298,19 @@ void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int line
// Called on all major tree changers. // Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals // Only for use for those really nasty bugs relating to internals
// Note this may be null. // Note this may be null.
// if (debug()) cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for " // if (debug() && nodep) cout << "-treeChange: V3Ast.cpp:" << lineno
// <<prefix<<": "<<cvtToHex(this)<<" <e"<<AstNode::s_editCntGbl<<">"<<endl; // << " Tree Change for " << prefix << ": "
// << cvtToHex(nodep) << " <e" << AstNode::s_editCntGbl << ">"
// << "m_iterpp=" << (void*)nodep->m_iterpp << endl;
// if (debug()) { // if (debug()) {
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl; // cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
// // Commenting out the section below may crash, as the tree state // // Commenting out the section below may crash, as the tree state
// // between edits is not always consistent for printing // // between edits is not always consistent for printing
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl; // cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
// v3Global.rootp()->dumpTree("- treeChange: "); // v3Global.rootp()->dumpTree("- treeChange: ");
// if (next||1) this->dumpTreeAndNext(cout, prefix); // if (next||1) nodep->dumpTreeAndNext(cout, prefix);
// else this->dumpTree(prefix); // else nodep->dumpTree(prefix);
// this->checkTree(); // nodep->checkTree();
// v3Global.rootp()->checkTree(); // v3Global.rootp()->checkTree();
//} //}
#endif #endif
@ -347,7 +349,8 @@ AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
newtailp->m_headtailp = headp; newtailp->m_headtailp = headp;
headp->m_headtailp = newtailp; headp->m_headtailp = newtailp;
newp->editCountInc(); newp->editCountInc();
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item // No change of m_iterpp, as only changing m_nextp of current node;
// the current node is still the one at the iteration point
} }
debugTreeChange(nodep, "-addNextOut:", __LINE__, true); debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
return nodep; return nodep;
@ -397,7 +400,8 @@ void AstNode::addNextHere(AstNode* newp) {
} // else is head, and we're inserting into the middle, so no other change } // else is head, and we're inserting into the middle, so no other change
} }
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item // No change of m_iterpp, as adding after current node;
// the current node is still the one at the iteration point
debugTreeChange(this, "-addHereOut: ", __LINE__, true); debugTreeChange(this, "-addHereOut: ", __LINE__, true);
} }
@ -565,8 +569,10 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
// Relink // Relink
oldp->m_backp = nullptr; oldp->m_backp = nullptr;
// Iterator fixup // Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr; if (oldp->m_iterpp) {
oldp->m_iterpp = nullptr; *(oldp->m_iterpp) = nullptr;
oldp->m_iterpp = nullptr;
}
debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true); debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true);
return oldp; return oldp;
} }
@ -580,7 +586,11 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
if (linkerp) { if (linkerp) {
linkerp->m_oldp = oldp; linkerp->m_oldp = oldp;
linkerp->m_backp = backp; linkerp->m_backp = backp;
linkerp->m_iterpp = oldp->m_iterpp; if (oldp->m_iterpp) { // Assumes we will always relink() if want to keep iterating
linkerp->m_iterpp = oldp->m_iterpp;
*(oldp->m_iterpp) = nullptr;
oldp->m_iterpp = nullptr;
}
if (backp->m_nextp == oldp) { if (backp->m_nextp == oldp) {
linkerp->m_chg = VNRelinker::RELINK_NEXT; linkerp->m_chg = VNRelinker::RELINK_NEXT;
} else if (backp->m_op1p == oldp) { } else if (backp->m_op1p == oldp) {
@ -625,12 +635,15 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
} }
} }
// Iterator fixup // Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = oldp->m_nextp; if (oldp->m_iterpp) { // Only if no linker, point to next in list
if (oldp->m_nextp) oldp->m_nextp->m_iterpp = oldp->m_iterpp;
*(oldp->m_iterpp) = oldp->m_nextp;
oldp->m_iterpp = nullptr;
}
// Relink // Relink
oldp->m_nextp = nullptr; oldp->m_nextp = nullptr;
oldp->m_backp = nullptr; oldp->m_backp = nullptr;
oldp->m_headtailp = this; oldp->m_headtailp = oldp;
oldp->m_iterpp = nullptr;
debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true); debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true);
return oldp; return oldp;
} }
@ -668,12 +681,12 @@ void AstNode::relink(VNRelinker* linkerp) {
// Iterator fixup // Iterator fixup
if (linkerp->m_iterpp) { if (linkerp->m_iterpp) {
// If we're iterating over a next() link, we need to follow links off the // If we're iterating over a next() link, we need to follow links off the
// NEW node. Thus we pass iteration information via a pointer in the node. // NEW node, which is always assumed to be what we are relinking to.
// This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing // This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing
// across every function. // across every function.
// If anyone has a cleaner way, I'd be grateful. // If anyone has a cleaner way, I'd be grateful.
*(linkerp->m_iterpp) = newp;
newp->m_iterpp = linkerp->m_iterpp; newp->m_iterpp = linkerp->m_iterpp;
*(newp->m_iterpp) = newp;
} }
// Empty the linker so not used twice accidentally // Empty the linker so not used twice accidentally
linkerp->m_backp = nullptr; linkerp->m_backp = nullptr;
@ -835,6 +848,7 @@ void AstNode::deleteNode() {
this->m_op2p = reinterpret_cast<AstNode*>(0x1); this->m_op2p = reinterpret_cast<AstNode*>(0x1);
this->m_op3p = reinterpret_cast<AstNode*>(0x1); this->m_op3p = reinterpret_cast<AstNode*>(0x1);
this->m_op4p = reinterpret_cast<AstNode*>(0x1); this->m_op4p = reinterpret_cast<AstNode*>(0x1);
this->m_iterpp = reinterpret_cast<AstNode**>(0x1);
if ( if (
#if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS) #if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS)
true true
@ -950,7 +964,7 @@ void AstNode::iterateAndNext(VNVisitor& v) {
niterp->m_iterpp = nullptr; niterp->m_iterpp = nullptr;
if (VL_UNLIKELY(niterp != nodep)) { // Edited node inside accept if (VL_UNLIKELY(niterp != nodep)) { // Edited node inside accept
nodep = niterp; nodep = niterp;
} else { // Unchanged node, just continue loop } else { // Unchanged node (though maybe updated m_next), just continue loop
nodep = niterp->m_nextp; nodep = niterp->m_nextp;
} }
} }
@ -1131,7 +1145,7 @@ void AstNode::checkIter() const {
if (m_iterpp) { if (m_iterpp) {
dumpPtrs(cout); dumpPtrs(cout);
// Perhaps something forgot to clear m_iterpp? // Perhaps something forgot to clear m_iterpp?
this->v3fatalSrc("Iteration link should be nullptr"); this->v3fatalSrc("Iteration link m_iterpp should be nullptr");
} }
} }

View File

@ -62,6 +62,8 @@ public:
} }
} s_brokenCntGlobal; } s_brokenCntGlobal;
static bool s_brokenAllowMidvisitorCheck = false;
//###################################################################### //######################################################################
// Table of allocated AstNode pointers // Table of allocated AstNode pointers
@ -172,6 +174,7 @@ private:
const char* const whyp = nodep->broken(); const char* const whyp = nodep->broken();
UASSERT_OBJ(!whyp, nodep, UASSERT_OBJ(!whyp, nodep,
"Broken link in node (or something without maybePointedTo): " << whyp); "Broken link in node (or something without maybePointedTo): " << whyp);
if (!s_brokenAllowMidvisitorCheck) nodep->checkIter();
if (nodep->dtypep()) { if (nodep->dtypep()) {
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep, UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
"Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep())); "Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
@ -351,6 +354,8 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
} }
} }
void V3Broken::allowMidvisitorCheck(bool flag) { s_brokenAllowMidvisitorCheck = flag; }
//###################################################################### //######################################################################
// Self test // Self test

View File

@ -32,6 +32,9 @@ public:
static bool isLinkable(const AstNode* nodep); static bool isLinkable(const AstNode* nodep);
static void addNewed(const AstNode* nodep); static void addNewed(const AstNode* nodep);
static void deleted(const AstNode* nodep); static void deleted(const AstNode* nodep);
// Called on error to say may be inside visitor,
// Disables checks that may misfire if not called at stable point between visitors
static void allowMidvisitorCheck(bool flag);
static void selfTest(); static void selfTest();
}; };

View File

@ -221,6 +221,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
} }
#ifndef V3ERROR_NO_GLOBAL_ #ifndef V3ERROR_NO_GLOBAL_
if (dumpTree() || debug()) { if (dumpTree() || debug()) {
V3Broken::allowMidvisitorCheck(true);
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) { V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
if (dumpTree()) { if (dumpTree()) {
v3Global.rootp()->dumpTreeFile( v3Global.rootp()->dumpTreeFile(