// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Ast node structures // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2020 by Wilson Snyder. 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. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Ast.h" #include "V3File.h" #include "V3Global.h" #include "V3Broken.h" #include "V3String.h" #include #include #include //====================================================================== // Statics vluint64_t AstNode::s_editCntLast = 0; vluint64_t AstNode::s_editCntGbl = 0; // Hot cache line // To allow for fast clearing of all user pointers, we keep a "timestamp" // along with each userp, and thus by bumping this count we can make it look // as if we iterated across the entire tree to set all the userp's to null. int AstNode::s_cloneCntGbl = 0; uint32_t AstUser1InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent uint32_t AstUser2InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent uint32_t AstUser3InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent uint32_t AstUser4InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent uint32_t AstUser5InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent bool AstUser1InUse::s_userBusy = false; bool AstUser2InUse::s_userBusy = false; bool AstUser3InUse::s_userBusy = false; bool AstUser4InUse::s_userBusy = false; bool AstUser5InUse::s_userBusy = false; int AstNodeDType::s_uniqueNum = 0; //###################################################################### // V3AstType std::ostream& operator<<(std::ostream& os, AstType rhs); //###################################################################### // Creators void AstNode::init() { editCountInc(); m_fileline = NULL; m_nextp = NULL; m_backp = NULL; m_headtailp = this; // When made, we're a list of only a single element m_op1p = NULL; m_op2p = NULL; m_op3p = NULL; m_op4p = NULL; m_iterpp = NULL; m_dtypep = NULL; m_clonep = NULL; m_cloneCnt = 0; // Attributes m_didWidth = false; m_doingWidth = false; m_protect = true; m_user1u = VNUser(0); m_user1Cnt = 0; m_user2u = VNUser(0); m_user2Cnt = 0; m_user3u = VNUser(0); m_user3Cnt = 0; m_user4u = VNUser(0); m_user4Cnt = 0; m_user5u = VNUser(0); m_user5Cnt = 0; } AstNode* AstNode::abovep() const { // m_headtailp only valid at beginning or end of list // Avoid supporting at other locations as would require walking // list which is likely to cause performance issues. UASSERT_OBJ(!m_nextp || firstAbovep(), this, "abovep() not allowed when in midlist"); const AstNode* firstp = firstAbovep() ? this : m_headtailp; return firstp->backp(); } string AstNode::encodeName(const string& namein) { // Encode signal name raw from parser, then not called again on same signal string out; for (string::const_iterator pos = namein.begin(); pos!=namein.end(); ++pos) { if ((pos==namein.begin()) ? isalpha(pos[0]) // digits can't lead identifiers : isalnum(pos[0])) { out += pos[0]; } else if (pos[0]=='_') { if (pos[1]=='_') { out += "_"; out += "__05F"; // hex(_) = 0x5F ++pos; if (pos==namein.end()) break; } else { out += pos[0]; } } else { // Need the leading 0 so this will never collide with // a user identifier nor a temp we create in Verilator. // We also do *NOT* use __DOT__ etc, as we search for those // in some replacements, and don't want to mangle the user's names. unsigned val = pos[0] & 0xff; // Mask to avoid sign extension char hex[10]; sprintf(hex, "__0%02X", val); out += hex; } } // Shorten names // TODO long term use VName in place of "string name" VName vname(out); out = vname.hashedName(); return out; } string AstNode::encodeNumber(vlsint64_t num) { if (num < 0) { return "__02D"+cvtToStr(-num); // 2D=- } else { return cvtToStr(num); } } string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); } string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); } string AstNode::shortName() const { string pretty = name(); string::size_type pos; while ((pos = pretty.find("__PVT__")) != string::npos) { pretty.replace(pos, 7, ""); } return pretty; } string AstNode::dedotName(const string& namein) { string pretty = namein; string::size_type pos; while ((pos = pretty.find("__DOT__")) != string::npos) { pretty.replace(pos, 7, "."); } if (pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, ""); return pretty; } string AstNode::vcdName(const string& namein) { // VCD tracing expects space to separate hierarchy // Dots are reserved for dots the user put in the name string pretty = namein; string::size_type pos; while ((pos = pretty.find("__DOT__")) != string::npos) { pretty.replace(pos, 7, " "); } while ((pos = pretty.find('.')) != string::npos) { pretty.replace(pos, 1, " "); } // Now convert escaped special characters, etc return prettyName(pretty); } string AstNode::prettyName(const string& namein) { // This function is somewhat hot, so we short-circuit some compares string pretty; pretty = ""; pretty.reserve(namein.length()); for (const char* pos = namein.c_str(); *pos; ) { if (pos[0]=='-' && pos[1]=='>') { // -> pretty += "."; pos += 2; continue; } if (pos[0]=='_' && pos[1]=='_') { // Short-circuit if (0==strncmp(pos, "__BRA__", 7)) { pretty += "["; pos += 7; continue; } if (0==strncmp(pos, "__KET__", 7)) { pretty += "]"; pos += 7; continue; } if (0==strncmp(pos, "__DOT__", 7)) { pretty += "."; pos += 7; continue; } if (0==strncmp(pos, "__PVT__", 7)) { pretty += ""; pos += 7; continue; } if (pos[0]=='_' && pos[1]=='_' && pos[2]=='0' && isxdigit(pos[3]) && isxdigit(pos[4])) { char value = 0; value += 16*(isdigit(pos[3]) ? (pos[3]-'0') : (tolower(pos[3])-'a'+10)); value += (isdigit(pos[4]) ? (pos[4]-'0') : (tolower(pos[4])-'a'+10)); pretty += value; pos += 5; continue; } } // Default pretty += pos[0]; ++pos; } if (pretty[0]=='T' && pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, ""); if (pretty[0]=='T' && pretty.substr(0, 5) == "TOP->") pretty.replace(0, 5, ""); return pretty; } string AstNode::prettyTypeName() const { if (name()=="") return typeName(); return string(typeName())+" '"+prettyName()+"'"; } //###################################################################### // Insertion inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next) { #ifdef VL_DEBUG // Called on all major tree changers. // Only for use for those really nasty bugs relating to internals // Note this may be null. //if (debug()) cout<<"-treeChange: V3Ast.cpp:"<"<dumpTree(cout, "-treeChange: "); // if (next||1) this->dumpTreeAndNext(cout, prefix); // else this->dumpTree(cout, prefix); // this->checkTree(); // v3Global.rootp()->checkTree(); //} #endif } AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) { // Add to m_nextp, returns this UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext");); nodep->debugTreeChange("-addNextThs: ", __LINE__, false); newp->debugTreeChange("-addNextNew: ", __LINE__, true); if (!nodep) { // verilog.y and lots of other places assume this return newp; } else { // Find end of old list AstNode* oldtailp = nodep; if (oldtailp->m_nextp) { if (oldtailp->m_headtailp) { oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep, "Node had next, but headtail says it shouldn't");); } else { // Though inefficient, we are occasionally passed a addNext in the middle of a list. while (oldtailp->m_nextp != NULL) oldtailp = oldtailp->m_nextp; } } // Link it in oldtailp->m_nextp = newp; newp->m_backp = oldtailp; // New tail needs the head AstNode* newtailp = newp->m_headtailp; AstNode* headp = oldtailp->m_headtailp; oldtailp->m_headtailp = NULL; // May be written again as new head newp->m_headtailp = NULL; // May be written again as new tail newtailp->m_headtailp = headp; headp->m_headtailp = newtailp; newp->editCountInc(); if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item } nodep->debugTreeChange("-addNextOut:", __LINE__, true); return nodep; } AstNode* AstNode::addNextNull(AstNode* nodep, AstNode* newp) { if (!newp) return nodep; return addNext(nodep, newp); } void AstNode::addNextHere(AstNode* newp) { // Add to m_nextp on exact node passed, not at the end. // This could be at head, tail, or both (single) // New could be head of single node, or list UASSERT(newp, "Null item passed to addNext"); UASSERT(!newp->backp(), "New node (back) already assigned?"); this->debugTreeChange("-addHereThs: ", __LINE__, false); newp->debugTreeChange("-addHereNew: ", __LINE__, true); newp->editCountInc(); AstNode* addlastp = newp->m_headtailp; // Last node in list to be added UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail"); // Forward links AstNode* oldnextp = this->m_nextp; this->m_nextp = newp; addlastp->m_nextp = oldnextp; // Perhaps null if 'this' is not list // Backward links if (oldnextp) oldnextp->m_backp = addlastp; newp->m_backp = this; // Head/tail AstNode* oldheadtailp = this->m_headtailp; // (!oldheadtailp) // this was&is middle of list // (oldheadtailp==this && !oldnext)// this was head AND tail (one node long list) // (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not tail // (oldheadtailp && !oldnextp) // this was tail of list, might also // be head of one-node list // newp->m_headtailp = NULL; // Not at head any longer addlastp->m_headtailp = NULL; // Presume middle of list // newp might happen to be head/tail after all, if so will be set again below if (oldheadtailp) { // else in middle of list, no change if (oldheadtailp==this) { // this was one node this->m_headtailp = addlastp; // Was head/tail, now a tail addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL) } else if (!oldnextp) { // this was tail this->m_headtailp = NULL; // No longer a tail oldheadtailp->m_headtailp = addlastp; // Head gets new tail addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL) } // 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 this->debugTreeChange("-addHereOut: ", __LINE__, true); } void AstNode::setOp1p(AstNode* newp) { UASSERT(newp, "Null item passed to setOp1p"); UDEBUGONLY(UASSERT_OBJ(!m_op1p, this, "Adding to non-empty, non-list op1");); UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node");); UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op1");); this->debugTreeChange("-setOp1pThs: ", __LINE__, false); newp->debugTreeChange("-setOp1pNew: ", __LINE__, true); m_op1p = newp; newp->editCountInc(); newp->m_backp = this; this->debugTreeChange("-setOp1pOut: ", __LINE__, false); } void AstNode::setOp2p(AstNode* newp) { UASSERT(newp, "Null item passed to setOp2p"); UDEBUGONLY(UASSERT_OBJ(!m_op2p, this, "Adding to non-empty, non-list op2");); UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node");); UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op2");); this->debugTreeChange("-setOp2pThs: ", __LINE__, false); newp->debugTreeChange("-setOp2pNew: ", __LINE__, true); m_op2p = newp; newp->editCountInc(); newp->m_backp = this; this->debugTreeChange("-setOp2pOut: ", __LINE__, false); } void AstNode::setOp3p(AstNode* newp) { UASSERT(newp, "Null item passed to setOp3p"); UDEBUGONLY(UASSERT_OBJ(!m_op3p, this, "Adding to non-empty, non-list op3");); UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node");); UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op3");); this->debugTreeChange("-setOp3pThs: ", __LINE__, false); newp->debugTreeChange("-setOp3pNew: ", __LINE__, true); m_op3p = newp; newp->editCountInc(); newp->m_backp = this; this->debugTreeChange("-setOp3pOut: ", __LINE__, false); } void AstNode::setOp4p(AstNode* newp) { UASSERT(newp, "Null item passed to setOp4p"); UDEBUGONLY(UASSERT_OBJ(!m_op4p, this, "Adding to non-empty, non-list op4");); UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node");); UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op4");); this->debugTreeChange("-setOp4pThs: ", __LINE__, false); newp->debugTreeChange("-setOp4pNew: ", __LINE__, true); m_op4p = newp; newp->editCountInc(); newp->m_backp = this; this->debugTreeChange("-setOp4pOut: ", __LINE__, false); } void AstNode::addOp1p(AstNode* newp) { UASSERT(newp, "Null item passed to addOp1p"); if (!m_op1p) { op1p(newp); } else { m_op1p->addNext(newp); } } void AstNode::addOp2p(AstNode* newp) { UASSERT(newp, "Null item passed to addOp2p"); if (!m_op2p) { op2p(newp); } else { m_op2p->addNext(newp); } } void AstNode::addOp3p(AstNode* newp) { UASSERT(newp, "Null item passed to addOp3p"); if (!m_op3p) { op3p(newp); } else { m_op3p->addNext(newp); } } void AstNode::addOp4p(AstNode* newp) { UASSERT(newp, "Null item passed to addOp4p"); if (!m_op4p) { op4p(newp); } else { m_op4p->addNext(newp); } } void AstNode::replaceWith(AstNode* newp) { // Replace oldp with this // Unlike a unlink/relink, children are changed to point to the new node. AstNRelinker repHandle; this->unlinkFrBack(&repHandle); repHandle.relink(newp); } void AstNRelinker::dump(std::ostream& str) const { str<<" BK="<(m_backp); str<<" ITER="<(m_iterpp); str<<" CHG="<<(m_chg==RELINK_NEXT?"[NEXT] ":""); str<<(m_chg==RELINK_OP1?"[OP1] ":""); str<<(m_chg==RELINK_OP2?"[OP2] ":""); str<<(m_chg==RELINK_OP3?"[OP3] ":""); str<<(m_chg==RELINK_OP4?"[OP4] ":""); } AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) { this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true); AstNode* oldp = this; UASSERT(oldp->m_backp, "Node has no back, already unlinked?"); oldp->editCountInc(); AstNode* backp = oldp->m_backp; if (linkerp) { linkerp->m_oldp = oldp; linkerp->m_backp = backp; linkerp->m_iterpp = oldp->m_iterpp; if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT; else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1; else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2; else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3; else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4; else oldp->v3fatalSrc("Unlink of node with back not pointing to it."); } if (backp->m_nextp== oldp) { backp->m_nextp= NULL; // Old list gets truncated // New list becomes a list upon itself // Most common case is unlinking a entire operand tree // (else we'd probably call unlinkFrBack without next) // We may be in the middle of a list; we have no way to find head or tail! AstNode* oldtailp = oldp; while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp; // Create new head/tail of old list AstNode* oldheadp = oldtailp->m_headtailp; oldheadp->m_headtailp = oldp->m_backp; oldheadp->m_headtailp->m_headtailp = oldheadp; // Create new head/tail of extracted list oldp->m_headtailp = oldtailp; oldp->m_headtailp->m_headtailp = oldp; } else if (backp->m_op1p == oldp) backp->m_op1p = NULL; else if (backp->m_op2p == oldp) backp->m_op2p = NULL; else if (backp->m_op3p == oldp) backp->m_op3p = NULL; else if (backp->m_op4p == oldp) backp->m_op4p = NULL; else this->v3fatalSrc("Unlink of node with back not pointing to it."); // Relink oldp->m_backp = NULL; // Iterator fixup if (oldp->m_iterpp) *(oldp->m_iterpp) = NULL; oldp->m_iterpp = NULL; oldp->debugTreeChange("-unlinkWNextOut: ", __LINE__, true); return oldp; } AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) { this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true); AstNode* oldp = this; UASSERT(oldp->m_backp, "Node has no back, already unlinked?"); oldp->editCountInc(); AstNode* backp = oldp->m_backp; if (linkerp) { linkerp->m_oldp = oldp; linkerp->m_backp = backp; linkerp->m_iterpp = oldp->m_iterpp; if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT; else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1; else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2; else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3; else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4; else this->v3fatalSrc("Unlink of node with back not pointing to it."); } if (backp->m_nextp== oldp) { // This node gets removed from middle (or tail) of list // Not head, since then oldp wouldn't be a next of backp... backp->m_nextp= oldp->m_nextp; if (backp->m_nextp) backp->m_nextp->m_backp = backp; // If it was a tail, back becomes new tail if (oldp->m_headtailp) { backp->m_headtailp = oldp->m_headtailp; backp->m_headtailp->m_headtailp = backp; } } else { if (backp->m_op1p == oldp) backp->m_op1p = oldp->m_nextp; else if (backp->m_op2p == oldp) backp->m_op2p = oldp->m_nextp; else if (backp->m_op3p == oldp) backp->m_op3p = oldp->m_nextp; else if (backp->m_op4p == oldp) backp->m_op4p = oldp->m_nextp; else this->v3fatalSrc("Unlink of node with back not pointing to it."); if (oldp->m_nextp) { AstNode* newheadp = oldp->m_nextp; newheadp->m_backp = backp; newheadp->m_headtailp = oldp->m_headtailp; newheadp->m_headtailp->m_headtailp = newheadp; } } // Iterator fixup if (oldp->m_iterpp) *(oldp->m_iterpp) = oldp->m_nextp; // Relink oldp->m_nextp = NULL; oldp->m_backp = NULL; oldp->m_headtailp = this; oldp->m_iterpp = NULL; oldp->debugTreeChange("-unlinkFrBkOut: ", __LINE__, true); return oldp; } void AstNode::relink(AstNRelinker* linkerp) { if (debug()>8) { UINFO(0," EDIT: relink: "); dumpPtrs(); } AstNode* newp = this; UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker"); UASSERT(!newp->backp(), "New node already linked?"); newp->editCountInc(); if (debug()>8) { linkerp->dump(cout); cout<m_backp; this->debugTreeChange("-relinkNew: ", __LINE__, true); backp->debugTreeChange("-relinkTre: ", __LINE__, true); switch (linkerp->m_chg) { case AstNRelinker::RELINK_NEXT: backp->addNextHere(newp); break; case AstNRelinker::RELINK_OP1: relinkOneLink(backp->m_op1p /*ref*/, newp); break; case AstNRelinker::RELINK_OP2: relinkOneLink(backp->m_op2p /*ref*/, newp); break; case AstNRelinker::RELINK_OP3: relinkOneLink(backp->m_op3p /*ref*/, newp); break; case AstNRelinker::RELINK_OP4: relinkOneLink(backp->m_op4p /*ref*/, newp); break; default: this->v3fatalSrc("Relink of node without any link to change."); break; } // Relink newp->m_backp = backp; linkerp->m_backp = NULL; // Iterator fixup if (linkerp->m_iterpp) { // 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. // This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing // across every function. // If anyone has a cleaner way, I'd be grateful. *(linkerp->m_iterpp) = newp; newp->m_iterpp = linkerp->m_iterpp; } // Empty the linker so not used twice accidentally linkerp->m_backp = NULL; this->debugTreeChange("-relinkOut: ", __LINE__, true); } void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp AstNode* newp) { if (pointpr) { // We know there will be at least two elements when we are done, // (newp & the old list). // We *ALLOW* the new node to have its own next list. // Likewise there may be a old list. // Insert the whole old list following the new node's list. // Thus a unlink without next, followed by relink, gives the same list. AstNode* newlistlastp = newp->m_headtailp; UASSERT_OBJ(!(newlistlastp->m_nextp && newlistlastp!=newp), newp, "Headtailp tail isn't at the tail"); AstNode* oldlistlastp = pointpr->m_headtailp; UASSERT_OBJ(!(oldlistlastp->m_nextp && oldlistlastp!=pointpr), newp, "Old headtailp tail isn't at the tail"); // Next links newlistlastp->m_nextp = pointpr; pointpr->m_backp = newlistlastp; // Head/tail pointpr->m_headtailp = NULL; // Old head newlistlastp->m_headtailp = NULL; // Old tail newp->m_headtailp = oldlistlastp; // Head points to tail oldlistlastp->m_headtailp = newp; // Tail points to head } pointpr = newp; } void AstNode::addHereThisAsNext(AstNode* newp) { // {old}->this->{next} becomes {old}->new->this->{next} AstNRelinker handle; this->unlinkFrBackWithNext(&handle); newp->addNext(this); handle.relink(newp); } void AstNode::swapWith(AstNode* bp) { AstNRelinker aHandle; AstNRelinker bHandle; this->unlinkFrBack(&aHandle); bp->unlinkFrBack(&bHandle); aHandle.relink(bp); bHandle.relink(this); } //====================================================================== // Clone AstNode* AstNode::cloneTreeIter() { // private: Clone single node and children AstNode* newp = this->clone(); if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList()); if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList()); if (this->m_op3p) newp->op3p(this->m_op3p->cloneTreeIterList()); if (this->m_op4p) newp->op4p(this->m_op4p->cloneTreeIterList()); newp->m_iterpp = NULL; newp->clonep(this); // Save pointers to/from both to simplify relinking. this->clonep(newp); // Save pointers to/from both to simplify relinking. return newp; } AstNode* AstNode::cloneTreeIterList() { // private: Clone list of nodes, set m_headtailp AstNode* newheadp = NULL; AstNode* newtailp = NULL; // Audited to make sure this is never NULL for (AstNode* oldp = this; oldp; oldp=oldp->m_nextp) { AstNode* newp = oldp->cloneTreeIter(); newp->m_headtailp = NULL; newp->m_backp = newtailp; if (newtailp) newtailp->m_nextp = newp; if (!newheadp) newheadp = newp; newtailp = newp; } newheadp->m_headtailp = newtailp; newtailp->m_headtailp = newheadp; return newheadp; } AstNode* AstNode::cloneTree(bool cloneNextLink) { this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink); cloneClearTree(); AstNode* newp; if (cloneNextLink && this->m_nextp) { newp = cloneTreeIterList(); } else { newp = cloneTreeIter(); newp->m_nextp = NULL; newp->m_headtailp = newp; } newp->m_backp = NULL; newp->cloneRelinkTree(); newp->debugTreeChange("-cloneOut: ", __LINE__, true); return newp; } //====================================================================== // Delete void AstNode::deleteNode() { // private: Delete single node. Publicly call deleteTree() instead. UASSERT(!m_backp, "Delete called on node with backlink still set"); editCountInc(); // Change links of old node so we coredump if used this->m_nextp = reinterpret_cast(0x1); this->m_backp = reinterpret_cast(0x1); this->m_headtailp = reinterpret_cast(0x1); this->m_op1p = reinterpret_cast(0x1); this->m_op2p = reinterpret_cast(0x1); this->m_op3p = reinterpret_cast(0x1); this->m_op4p = reinterpret_cast(0x1); if ( #if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS) 1 #else !v3Global.opt.debugLeak() #endif ) { delete this; } // Else leak massively, so each pointer is unique // and we can debug easier. } void AstNode::deleteTreeIter() { // private: Delete list of nodes. Publicly call deleteTree() instead. // Audited to make sure this is never NULL for (AstNode* nodep=this, *nnextp; nodep; nodep=nnextp) { nnextp = nodep->m_nextp; // MUST be depth first! if (nodep->m_op1p) nodep->m_op1p->deleteTreeIter(); if (nodep->m_op2p) nodep->m_op2p->deleteTreeIter(); if (nodep->m_op3p) nodep->m_op3p->deleteTreeIter(); if (nodep->m_op4p) nodep->m_op4p->deleteTreeIter(); nodep->m_nextp = NULL; nodep->m_backp = NULL; nodep->deleteNode(); } } void AstNode::deleteTree() { // deleteTree always deletes the next link, because you must have called // unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this. UASSERT(!m_backp, "Delete called on node with backlink still set"); this->debugTreeChange("-delTree: ", __LINE__, true); this->editCountInc(); // MUST be depth first! deleteTreeIter(); } //====================================================================== // Memory checks #ifdef VL_LEAK_CHECKS void* AstNode::operator new(size_t size) { // Optimization note: Aligning to cache line is a loss, due to lost packing 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 void AstNode::iterateChildren(AstNVisitor& v) { // This is a very hot function // Optimization note: Grabbing m_op#p->m_nextp is a net loss ASTNODE_PREFETCH(m_op1p); ASTNODE_PREFETCH(m_op2p); ASTNODE_PREFETCH(m_op3p); ASTNODE_PREFETCH(m_op4p); if (m_op1p) m_op1p->iterateAndNext(v); if (m_op2p) m_op2p->iterateAndNext(v); if (m_op3p) m_op3p->iterateAndNext(v); if (m_op4p) m_op4p->iterateAndNext(v); } void AstNode::iterateChildrenConst(AstNVisitor& v) { // This is a very hot function ASTNODE_PREFETCH(m_op1p); ASTNODE_PREFETCH(m_op2p); ASTNODE_PREFETCH(m_op3p); ASTNODE_PREFETCH(m_op4p); if (m_op1p) m_op1p->iterateAndNextConst(v); if (m_op2p) m_op2p->iterateAndNextConst(v); if (m_op3p) m_op3p->iterateAndNextConst(v); if (m_op4p) m_op4p->iterateAndNextConst(v); } void AstNode::iterateAndNext(AstNVisitor& v) { // This is a very hot function // IMPORTANT: If you replace a node that's the target of this iterator, // then the NEW node will be iterated on next, it isn't skipped! // Future versions of this function may require the node to have a back to be iterated; // there's no lower level reason yet though the back must exist. AstNode* nodep = this; #ifdef VL_DEBUG // Otherwise too hot of a function for debug UASSERT_OBJ(!(nodep && !nodep->m_backp), nodep, "iterateAndNext node has no back"); #endif if (nodep) ASTNODE_PREFETCH(nodep->m_nextp); while (nodep) { // effectively: if (!this) return; // Callers rely on this if (nodep->m_nextp) ASTNODE_PREFETCH(nodep->m_nextp->m_nextp); AstNode* niterp = nodep; // This address may get stomped via m_iterpp if the node is edited // Desirable check, but many places where multiple iterations are OK // UASSERT_OBJ(!niterp->m_iterpp, niterp, "IterateAndNext under iterateAndNext may miss edits"); // Optimization note: Doing PREFETCH_RW on m_iterpp is a net even // cppcheck-suppress nullPointer niterp->m_iterpp = &niterp; niterp->accept(v); // accept may do a replaceNode and change niterp on us... // niterp maybe NULL, so need cast if printing //if (niterp != nodep) UINFO(1,"iterateAndNext edited "<m_iterpp = NULL; if (VL_UNLIKELY(niterp!=nodep)) { // Edited node inside accept nodep = niterp; } else { // Unchanged node, just continue loop nodep = niterp->m_nextp; } } } void AstNode::iterateListBackwards(AstNVisitor& v) { AstNode* nodep = this; while (nodep->m_nextp) nodep = nodep->m_nextp; while (nodep) { // Edits not supported: nodep->m_iterpp = &nodep; nodep->accept(v); if (nodep->backp()->m_nextp == nodep) nodep = nodep->backp(); else nodep = NULL; // else: backp points up the tree. } } void AstNode::iterateChildrenBackwards(AstNVisitor& v) { if (m_op1p) m_op1p->iterateListBackwards(v); if (m_op2p) m_op2p->iterateListBackwards(v); if (m_op3p) m_op3p->iterateListBackwards(v); if (m_op4p) m_op4p->iterateListBackwards(v); } void AstNode::iterateAndNextConst(AstNVisitor& v) { // Keep following the current list even if edits change it AstNode* nodep = this; do { AstNode* nnextp = nodep->m_nextp; ASTNODE_PREFETCH(nnextp); nodep->accept(v); nodep = nnextp; } while (nodep); } AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) { // Some visitors perform tree edits (such as V3Const), and may even // replace/delete the exact nodep that the visitor is called with. If // this happens, the parent will lose the handle to the node that was // processed. // To solve this, this function returns the pointer to the replacement node, // which in many cases is just the same node that was passed in. AstNode* nodep = this; // Note "this" may point to bogus point later in this function if (VN_IS(nodep, Netlist)) { // Calling on top level; we know the netlist won't get replaced nodep->accept(v); } else if (!nodep->backp()) { // Calling on standalone tree; insert a shim node so we can keep // track, then delete it on completion AstBegin* tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep); { VL_DO_DANGLING(tempp->stmtsp()->accept(v), nodep); // nodep to null as may be replaced } nodep = tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } else { // Use back to determine who's pointing at us (IE assume new node // grafts into same place as old one) AstNode** nextnodepp = NULL; if (this->m_backp->m_op1p == this) nextnodepp = &(this->m_backp->m_op1p); else if (this->m_backp->m_op2p == this) nextnodepp = &(this->m_backp->m_op2p); else if (this->m_backp->m_op3p == this) nextnodepp = &(this->m_backp->m_op3p); else if (this->m_backp->m_op4p == this) nextnodepp = &(this->m_backp->m_op4p); else if (this->m_backp->m_nextp == this) nextnodepp = &(this->m_backp->m_nextp); UASSERT_OBJ(nextnodepp, this, "Node's back doesn't point to forward to node itself"); { VL_DO_DANGLING(nodep->accept(v), nodep); // nodep to null as may be replaced } nodep = *nextnodepp; // Grab new node from point where old was connected } return nodep; } //====================================================================== void AstNode::cloneRelinkTree() { // private: Cleanup clone() operation on whole tree. Publicly call cloneTree() instead. for (AstNode* nodep=this; nodep; nodep = nodep->m_nextp) { if (m_dtypep && m_dtypep->clonep()) { m_dtypep = m_dtypep->clonep(); } nodep->cloneRelink(); if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree(); if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree(); if (nodep->m_op3p) nodep->m_op3p->cloneRelinkTree(); if (nodep->m_op4p) nodep->m_op4p->cloneRelinkTree(); } } //====================================================================== // Comparison bool AstNode::gateTreeIter() const { // private: Return true if the two trees are identical if (!isGateOptimizable()) return false; if (m_op1p && !m_op1p->gateTreeIter()) return false; if (m_op2p && !m_op2p->gateTreeIter()) return false; if (m_op3p && !m_op3p->gateTreeIter()) return false; if (m_op4p && !m_op4p->gateTreeIter()) return false; return true; } bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly) { // private: Return true if the two trees are identical if (!node1p && !node2p) return true; if (!node1p || !node2p) return false; if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep() || !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) { return false; } return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly) && sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly) && sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly) && sameTreeIter(node1p->m_op4p, node2p->m_op4p, false, gateOnly) && (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly)) ); } //====================================================================== // Static utilities std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) { return os<backp(), this, "Back node inconsistent"); if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) { // Termops have a short-circuited iterateChildren, so check usage UASSERT_OBJ(!(op1p()||op2p()||op3p()||op4p()), this, "Terminal operation with non-terminals"); } if (m_op1p) m_op1p->checkTreeIterList(this); if (m_op2p) m_op2p->checkTreeIterList(this); if (m_op3p) m_op3p->checkTreeIterList(this); if (m_op4p) m_op4p->checkTreeIterList(this); } void AstNode::checkTreeIterList(AstNode* backp) { // private: Check a (possible) list of nodes, this is always the head of the list // Audited to make sure this is never NULL AstNode* headp = this; AstNode* tailp = this; for (AstNode* nodep=headp; nodep; nodep=nodep->nextp()) { nodep->checkTreeIter(backp); UASSERT_OBJ(headp==this || !nextp(), this, "Headtailp should be null in middle of lists"); tailp = nodep; backp = nodep; } UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent"); UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent"); } void AstNode::checkTree() { if (!debug()) return; if (this->backp()) { // Linked tree- check only the passed node this->checkTreeIter(this->backp()); } else { this->checkTreeIterList(this->backp()); } } // cppcheck-suppress unusedFunction // Debug only void AstNode::dumpGdb() { // For GDB only // LCOV_EXCL_LINE dumpGdbHeader(); // LCOV_EXCL_LINE cout<<" "; dump(cout); cout<dumpTreeFile(filename); // LCOV_EXCL_LINE } // cppcheck-suppress unusedFunction // Debug only void AstNode::checkIter() const { if (m_iterpp) { dumpPtrs(cout); // Perhaps something forgot to clear m_iterpp? this->v3fatalSrc("Iteration link should be NULL"); } } void AstNode::dumpPtrs(std::ostream& os) const { os<<"This="<8) { os<= 9) { os<warnContextSecondary(); } if (maxDepth==1) { if (op1p()||op2p()||op3p()||op4p()) { os<nextp()) { nodep->dumpTree(os, indent+"1:", maxDepth-1); } for (const AstNode* nodep=op2p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os, indent+"2:", maxDepth-1); } for (const AstNode* nodep=op3p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os, indent+"3:", maxDepth-1); } for (const AstNode* nodep=op4p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os, indent+"4:", maxDepth-1); } } } void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDepth) const { // Audited to make sure this is never NULL for (const AstNode* nodep = this; nodep; nodep = nodep->nextp()) { nodep->dumpTree(os, indent, maxDepth); } } void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) { // Not const function as calls checkTree if (doDump) { { // Write log & close UINFO(2,"Dumping "< logsp (V3File::new_ofstream(filename, append)); if (logsp->fail()) v3fatal("Can't write "<"; *logsp<<" to "<=9)) { *logsp<isTop()) break; str += scopep->prettyName(); return str; } backp = backp->backp(); } backp = this; while (backp) { const AstModule* modp; const AstNodeVarRef* nvrp; if ((modp = VN_CAST_CONST(backp, Module)) && !modp->hierName().empty()) { str += modp->hierName(); return str; } else if ((nvrp = VN_CAST_CONST(backp, NodeVarRef))) { string prettyName = nvrp->prettyName(); // VarRefs have not been flattened yet and do not contain location information if (prettyName != nvrp->name()) { str += prettyName; return str; } } backp = backp->backp(); } return ""; } void AstNode::v3errorEnd(std::ostringstream& str) const { if (!m_fileline) { V3Error::v3errorEnd(str, locationStr()); } else { std::ostringstream nsstr; nsstr<(this)->dump(nsstr); nsstr<v3errorEnd(nsstr, locationStr()); } } //====================================================================== // Data type conversion void AstNode::dtypeChgSigned(bool flag) { UASSERT_OBJ(dtypep(), this, "No dtype when changing to (un)signed"); dtypeChgWidthSigned(dtypep()->width(), dtypep()->widthMin(), AstNumeric::fromBool(flag)); } void AstNode::dtypeChgWidth(int width, int widthMin) { UASSERT_OBJ(dtypep(), this, "No dtype when changing width"); // Use ChgWidthSigned(...UNSIGNED) otherwise dtypeChgWidthSigned(width, widthMin, dtypep()->numeric()); } void AstNode::dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric) { if (!dtypep()) { // We allow dtypep() to be null, as before/during widthing dtypes are not resolved dtypeSetLogicUnsized(width, widthMin, numeric); } else { if (width==dtypep()->width() && widthMin==dtypep()->widthMin() && numeric==dtypep()->numeric()) return; // Correct already // FUTURE: We may be pointing at a two state data type, and this may // convert it to logic. Since the AstVar remains correct, we // work OK but this assumption may break in the future. // Note we can't just clone and do a widthForce, as if it's a BasicDType // the msb() indications etc will be incorrect. dtypeSetLogicUnsized(width, widthMin, numeric); } } AstNodeDType* AstNode::findBasicDType(AstBasicDTypeKwd kwd) const { // For 'simple' types we use the global directory. These are all unsized. // More advanced types land under the module/task/etc return v3Global.rootp()->typeTablep() ->findBasicDType(fileline(), kwd); } AstNodeDType* AstNode::findBitDType(int width, int widthMin, AstNumeric numeric) const { return v3Global.rootp()->typeTablep() ->findLogicBitDType(fileline(), AstBasicDTypeKwd::BIT, width, widthMin, numeric); } AstNodeDType* AstNode::findLogicDType(int width, int widthMin, AstNumeric numeric) const { return v3Global.rootp()->typeTablep() ->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, width, widthMin, numeric); } AstNodeDType* AstNode::findLogicRangeDType(VNumRange range, int widthMin, AstNumeric numeric) const { return v3Global.rootp()->typeTablep() ->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, range, widthMin, numeric); } AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { return v3Global.rootp()->typeTablep() ->findInsertSameDType(nodep); } AstNodeDType* AstNode::findVoidDType() const { return v3Global.rootp()->typeTablep() ->findVoidDType(fileline()); } //###################################################################### // AstNVisitor void AstNVisitor::doDeletes() { for (std::vector::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) { (*it)->deleteTree(); } m_deleteps.clear(); }