// -*- 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 "V3Graph.h" #include "V3PartitionGraph.h" // Just for mtask dumping #include #include #include //====================================================================== // Special methods // We need these here, because the classes they point to aren't defined when we declare the class const char* AstIfaceRefDType::broken() const { BROKEN_RTN(m_ifacep && !m_ifacep->brokeExists()); BROKEN_RTN(m_cellp && !m_cellp->brokeExists()); BROKEN_RTN(m_modportp && !m_modportp->brokeExists()); return NULL; } AstIface* AstIfaceRefDType::ifaceViaCellp() const { return ((m_cellp && m_cellp->modp()) ? VN_CAST(m_cellp->modp(), Iface) : m_ifacep); } const char* AstNodeVarRef::broken() const { BROKEN_RTN(m_varScopep && !m_varScopep->brokeExists()); BROKEN_RTN(m_varp && !m_varp->brokeExists()); return NULL; } void AstNodeVarRef::cloneRelink() { if (m_varp && m_varp->clonep()) { m_varp = m_varp->clonep(); } } string AstNodeVarRef::hiernameProtect() const { return VIdProtect::protectWordsIf(hiername(), protect()); } int AstNodeSel::bitConst() const { AstConst* constp = VN_CAST(bitp(), Const); return (constp ? constp->toSInt() : 0); } void AstNodeUOrStructDType::repairMemberCache() { clearCache(); for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { if (m_members.find(itemp->name())!=m_members.end()) { itemp->v3error("Duplicate declaration of member name: "<prettyNameQ()); } else m_members.insert(make_pair(itemp->name(), itemp)); } } const char* AstNodeUOrStructDType::broken() const { vl_unordered_set exists; for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { exists.insert(itemp); } for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) { if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) { this->v3error("Internal: Structure member broken: "<first); return "member broken"; } } return NULL; } void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, const V3Number& ths) { if (lhs.isNeqZero()) out.opAssign(rhs); else out.opAssign(ths); } int AstBasicDType::widthAlignBytes() const { if (width()<=8) return 1; else if (width()<=16) return 2; else if (isQuad()) return 8; else return 4; } int AstBasicDType::widthTotalBytes() const { if (width()<=8) return 1; else if (width()<=16) return 2; else if (isQuad()) return 8; else return widthWords() * (VL_EDATASIZE / 8); } int AstNodeUOrStructDType::widthTotalBytes() const { if (width()<=8) return 1; else if (width()<=16) return 2; else if (isQuad()) return 8; else return widthWords() * (VL_EDATASIZE / 8); } int AstNodeUOrStructDType::widthAlignBytes() const { // Could do max across members but that would be slow, // instead intuit based on total structure size if (width()<=8) return 1; else if (width()<=16) return 2; else if (width()<=32) return 4; else return 8; } AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isString() && rhsp->isString()) { return new AstEqN(fl, lhsp, rhsp); } else if (lhsp->isDouble() && rhsp->isDouble()) { return new AstEqD(fl, lhsp, rhsp); } else { return new AstEq(fl, lhsp, rhsp); } } AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isString() && rhsp->isString()) { return new AstGteN(fl, lhsp, rhsp); } else if (lhsp->isDouble() && rhsp->isDouble()) { return new AstGteD(fl, lhsp, rhsp); } else if (lhsp->isSigned() && rhsp->isSigned()) { return new AstGteS(fl, lhsp, rhsp); } else { return new AstGte(fl, lhsp, rhsp); } } AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isString() && rhsp->isString()) { return new AstLteN(fl, lhsp, rhsp); } else if (lhsp->isDouble() && rhsp->isDouble()) { return new AstLteD(fl, lhsp, rhsp); } else if (lhsp->isSigned() && rhsp->isSigned()) { return new AstLteS(fl, lhsp, rhsp); } else { return new AstLte(fl, lhsp, rhsp); } } AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isString() && rhsp->isString()) { return new AstEqN(fl, lhsp, rhsp); } else if (lhsp->isDouble() && rhsp->isDouble()) { return new AstEqD(fl, lhsp, rhsp); } else { return new AstEqWild(fl, lhsp, rhsp); } } AstExecGraph::AstExecGraph(FileLine* fileline) : AstNode(fileline) { m_depGraphp = new V3Graph; } AstExecGraph::~AstExecGraph() { delete m_depGraphp; VL_DANGLING(m_depGraphp); } bool AstVar::isSigPublic() const { return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar())); } bool AstVar::isScQuad() const { return (isSc() && isQuad() && !isScBv() && !isScBigUint()); } bool AstVar::isScBv() const { return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv); } bool AstVar::isScUint() const { return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv()); } bool AstVar::isScBigUint() const { return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv()); } void AstVar::combineType(AstVarType type) { // These flags get combined with the existing settings of the flags. // We don't test varType for certain types, instead set flags since // when we combine wires cross-hierarchy we need a union of all characteristics. m_varType = type; // These flags get combined with the existing settings of the flags. if (type==AstVarType::TRIWIRE || type==AstVarType::TRI0 || type==AstVarType::TRI1) { m_tristate = true; } if (type==AstVarType::TRI0) { m_isPulldown = true; } if (type==AstVarType::TRI1) { m_isPullup = true; } } string AstVar::verilogKwd() const { if (isIO()) { return direction().verilogKwd(); } else if (isTristate()) { return "tri"; } else if (varType()==AstVarType::WIRE) { return "wire"; } else if (varType()==AstVarType::WREAL) { return "wreal"; } else if (varType()==AstVarType::IFACEREF) { return "ifaceref"; } else { return dtypep()->name(); } } class AstVar::VlArgTypeRecursed { public: string m_oref; // To output, reference part before name, "&" string m_osuffix; // To output, suffixed after name, "[3]" string m_oprefix; // To output, prefixed before name, "Foo_t" void clear() { m_oref.clear(); m_osuffix.clear(); m_oprefix.clear(); } string refParen(const string& name) { return m_oref.empty() ? name : "("+m_oref+" "+name+")"; } }; string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc) const { UASSERT_OBJ(!forReturn, this, "Internal data is never passed as return, but as first argument"); string ostatic; if (isStatic() && namespc.empty()) ostatic = "static "; VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, dtypep(), false); string oname; if (named) { oname += " "; if (!namespc.empty()) oname += namespc+"::"; oname += VIdProtect::protectIf(name(), protect()); } return ostatic + info.m_oprefix + info.refParen(oname) + info.m_osuffix; } AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep, bool arrayed) const { dtypep = dtypep->skipRefp(); if (const AstAssocArrayDType* adtypep = VN_CAST_CONST(dtypep, AssocArrayDType)) { VlArgTypeRecursed key = vlArgTypeRecurse(forFunc, adtypep->keyDTypep(), true); VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true); string out = "VlAssocArray<"; out += key.m_oprefix; if (!key.m_osuffix.empty() || !key.m_oref.empty()) { out += " " + key.m_osuffix + key.m_oref; } out += ", "; out += sub.m_oprefix; if (!sub.m_osuffix.empty() || !sub.m_oref.empty()) { out += " " + sub.m_osuffix + sub.m_oref; } out += "> "; VlArgTypeRecursed info; info.m_oprefix = out; return info; } else if (const AstQueueDType* adtypep = VN_CAST_CONST(dtypep, QueueDType)) { VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true); VlArgTypeRecursed info; string out = "VlQueue<" + sub.m_oprefix; if (!sub.m_osuffix.empty() || !sub.m_oref.empty()) { out += " " + sub.m_osuffix + sub.m_oref; } // + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1 if (adtypep->boundp()) out += ", " + cvtToStr(adtypep->boundConst() + 1); out += "> "; info.m_oprefix = out; return info; } else if (const AstUnpackArrayDType* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) { VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), arrayed); info.m_osuffix = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + info.m_osuffix; return info; } else if (const AstBasicDType* bdtypep = dtypep->basicp()) { string otype; string oarray; bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING; string bitvec; if (bdtypep && !bdtypep->isOpaque() && !v3Global.opt.protectIds()) { // We don't print msb()/lsb() as multidim packed would require recursion, // and may confuse users as C++ data is stored always with bit 0 used bitvec += "/*"+cvtToStr(dtypep->width()-1)+":0*/"; } if ((forFunc && isReadOnly()) || bdtypep->keyword() == AstBasicDTypeKwd::CHARPTR || bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) otype += "const "; if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::CHARPTR) { otype += "char*"; } else if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) { otype += "VerilatedScope*"; } else if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::DOUBLE) { otype += "double"; } else if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::FLOAT) { otype += "float"; } else if (strtype) { otype += "std::string"; } else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width otype += "CData"+bitvec; } else if (dtypep->widthMin() <= 16) { otype += "SData"+bitvec; } else if (dtypep->widthMin() <= VL_IDATASIZE) { otype += "IData"+bitvec; } else if (dtypep->isQuad()) { otype += "QData"+bitvec; } else if (dtypep->isWide()) { if (arrayed) { otype += "VlWide<"+cvtToStr(dtypep->widthWords())+"> "; } else { otype += "WData"+bitvec; // []'s added later oarray += "["+cvtToStr(dtypep->widthWords())+"]"; } } string oref; if (isDpiOpenArray() || (forFunc && (isWritable() || direction() == VDirection::REF || direction() == VDirection::CONSTREF || (strtype && isNonOutput())))) { oref = "&"; } VlArgTypeRecursed info; info.m_oprefix = otype; info.m_osuffix = oarray; info.m_oref = oref; //UINFO(9, "vlArgRec "<<"oprefix="<prettyName()); } } string AstVar::vlEnumType() const { string arg; AstBasicDType* bdtypep = basicp(); bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING; if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) { return "VLVT_PTR"; } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) { return "VLVT_PTR"; } else if (strtype) { arg += "VLVT_STRING"; } else if (widthMin() <= 8) { arg += "VLVT_UINT8"; } else if (widthMin() <= 16) { arg += "VLVT_UINT16"; } else if (widthMin() <= VL_IDATASIZE) { arg += "VLVT_UINT32"; } else if (isQuad()) { arg += "VLVT_UINT64"; } else if (isWide()) { arg += "VLVT_WDATA"; } // else return "VLVT_UNKNOWN" return arg; } string AstVar::vlEnumDir() const { string out; if (isInoutish()) { out = "VLVD_INOUT"; } else if (isWritable()) { out = "VLVD_OUT"; } else if (isNonOutput()) { out = "VLVD_IN"; } else { out = "VLVD_NODIR"; } // if (isSigUserRWPublic()) out += "|VLVF_PUB_RW"; else if (isSigUserRdPublic()) out += "|VLVF_PUB_RD"; // if (AstBasicDType* bdtypep = basicp()) { if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY"; } return out; } string AstVar::vlPropInit() const { string out; out = vlEnumType(); // VLVT_UINT32 etc out += ", "+vlEnumDir(); // VLVD_IN etc if (AstBasicDType* bdtypep = basicp()) { out += ", VerilatedVarProps::Packed()"; out += ", "+cvtToStr(bdtypep->left())+", "+cvtToStr(bdtypep->right()); } bool first = true; for (AstNodeDType* dtp=dtypep(); dtp; ) { dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (AstNodeArrayDType* adtypep = VN_CAST(dtp, NodeArrayDType)) { if (first) { out += ", VerilatedVarProps::Unpacked()"; first = false; } out += ", "+cvtToStr(adtypep->declRange().left())+", "+cvtToStr(adtypep->declRange().right()); dtp = adtypep->subDTypep(); } else break; // AstBasicDType - nothing below } return out; } string AstVar::cPubArgType(bool named, bool forReturn) const { if (forReturn) named = false; string arg; if (isWide() && isReadOnly()) arg += "const "; if (widthMin() == 1) { arg += "bool"; } else if (widthMin() <= VL_IDATASIZE) { arg += "uint32_t"; } else if (widthMin() <= VL_QUADSIZE) { arg += "vluint64_t"; } else { arg += "uint32_t"; // []'s added later } if (isWide()) { if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead"); arg += " (& "+name(); arg += ")["+cvtToStr(widthWords())+"]"; } else { if (!forReturn && (isWritable() || direction().isRefOrConstRef())) arg += "&"; if (named) arg += " "+name(); } return arg; } string AstVar::dpiArgType(bool named, bool forReturn) const { if (forReturn) named = false; string arg; if (isDpiOpenArray()) { arg = "const svOpenArrayHandle"; } else if (!basicp()) { arg = "UNKNOWN"; } else if (basicp()->keyword().isDpiBitVal()) { if (widthMin() == 1) { arg = "unsigned char"; if (!forReturn && isWritable()) arg += "*"; } else { if (forReturn) { arg = "svBitVecVal"; } else if (isReadOnly()) { arg = "const svBitVecVal*"; } else { arg = "svBitVecVal*"; } } } else if (basicp()->keyword().isDpiLogicVal()) { if (widthMin() == 1) { arg = "unsigned char"; if (!forReturn && isWritable()) arg += "*"; } else { if (forReturn) { arg = "svLogicVecVal"; } else if (isReadOnly()) { arg = "const svLogicVecVal*"; } else { arg = "svLogicVecVal*"; } } } else { arg = basicp()->keyword().dpiType(); if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) { arg = "unsigned "+arg; } if (!forReturn && isWritable()) arg += "*"; } if (named) arg += " "+name(); return arg; } string AstVar::scType() const { if (isScBigUint()) { return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> } else if (isScUint()) { return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> } else if (isScBv()) { return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> } else if (widthMin() == 1) { return "bool"; } else if (widthMin() <= VL_IDATASIZE) { if (widthMin() <= 8 && v3Global.opt.pinsUint8()) { return "uint8_t"; } else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) { return "uint16_t"; } else { return "uint32_t"; } } else { return "vluint64_t"; } } AstVar* AstVar::scVarRecurse(AstNode* nodep) { // See if this is a SC assignment; if so return that type // Historically sc variables are identified by a variable // attribute. TODO it would better be a data type attribute. if (AstVar* anodep = VN_CAST(nodep, Var)) { if (anodep->isSc()) return anodep; else return NULL; } else if (VN_IS(nodep, VarRef)) { if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp(); else return NULL; } else if (VN_IS(nodep, ArraySel)) { if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p; if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p; if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p; if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p; } return NULL; } string AstVar::mtasksString() const { std::ostringstream os; os<<"all: "; for (MTaskIdSet::const_iterator it = m_mtaskIds.begin(); it != m_mtaskIds.end(); ++it) { os<<*it<<" "; } return os.str(); } AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) { // dimension passed from AstArraySel::dimension // Dimension 0 means the VAR itself, 1 is the closest SEL to the AstVar, // which is the lowest in the dtype list. // ref order: a[1][2][3][4] // Created as: reg [4] a [1][2][3]; // *or* reg a [1][2][3][4]; // // The bit select is optional; used only if "leftover" []'s // SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a)))) // DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3)))) // *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT)))) // SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0 // TODO this function should be removed in favor of recursing the dtype(), // as that allows for more complicated data types. int dim = 0; for (AstNodeDType* dtypep=this; dtypep; ) { dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) { if ((dim++)==dimension) { return dtypep; } dtypep = adtypep->subDTypep(); continue; } else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) { // AstBasicDType - nothing below, return null if (adtypep->isRanged()) { if ((dim++) == dimension) { return adtypep; } } return NULL; } else if (AstNodeUOrStructDType* adtypep = VN_CAST(dtypep, NodeUOrStructDType)) { if (adtypep->packed()) { if ((dim++) == dimension) { return adtypep; } } return NULL; } // Node no ->next in loop; use continue where necessary break; } return NULL; } uint32_t AstNodeDType::arrayUnpackedElements() { uint32_t entries = 1; for (AstNodeDType* dtypep=this; dtypep; ) { dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) { entries *= adtypep->elementsConst(); dtypep = adtypep->subDTypep(); } else { // AstBasicDType - nothing below, 1 break; } } return entries; } std::pair AstNodeDType::dimensions(bool includeBasic) { // How many array dimensions (packed,unpacked) does this Var have? uint32_t packed = 0; uint32_t unpacked = 0; for (AstNodeDType* dtypep=this; dtypep; ) { dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) { if (VN_IS(adtypep, PackArrayDType)) packed++; else unpacked++; dtypep = adtypep->subDTypep(); continue; } else if (const AstQueueDType* qdtypep = VN_CAST(dtypep, QueueDType)) { unpacked++; dtypep = qdtypep->subDTypep(); continue; } else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) { if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++; } else if (const AstStructDType* sdtypep = VN_CAST(dtypep, StructDType)) { packed++; } break; } return make_pair(packed, unpacked); } int AstNodeDType::widthPow2() const { // I.e. width 30 returns 32, width 32 returns 32. uint32_t width = this->width(); for (int p2=30; p2>=0; p2--) { if (width > (1UL<fromp(); continue; } else if (VN_IS(nodep, Sel)) { nodep = VN_CAST(nodep, Sel)->fromp(); continue; } // AstNodeSelPre stashes the associated variable under an ATTROF // of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified else if (VN_IS(nodep, AttrOf)) { nodep = VN_CAST(nodep, AttrOf)->fromp(); continue; } else if (VN_IS(nodep, NodePreSel)) { if (VN_CAST(nodep, NodePreSel)->attrp()) { nodep = VN_CAST(nodep, NodePreSel)->attrp(); } else { nodep = VN_CAST(nodep, NodePreSel)->lhsp(); } continue; } else break; } return nodep; } string AstCCall::hiernameProtect() const { return VIdProtect::protectWordsIf(hiername(), protect()); } const char* AstScope::broken() const { BROKEN_RTN(m_aboveScopep && !m_aboveScopep->brokeExists()); BROKEN_RTN(m_aboveCellp && !m_aboveCellp->brokeExists()); BROKEN_RTN(!m_modp); BROKEN_RTN(m_modp && !m_modp->brokeExists()); return NULL; } void AstScope::cloneRelink() { if (m_aboveScopep && m_aboveScopep->clonep()) m_aboveScopep->clonep(); if (m_aboveCellp && m_aboveCellp->clonep()) m_aboveCellp->clonep(); if (m_modp && static_cast(m_modp)->clonep()) { static_cast(m_modp)->clonep(); } } string AstScope::nameDotless() const { string out = shortName(); string::size_type pos; while ((pos = out.find('.')) != string::npos) { out.replace(pos, 1, "__"); } return out; } string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const { string out; for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) { out += textp->text(); } // TOP will be replaced by top->name() if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, ""); if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, ""); if (out.substr(0, 1) == ".") out.replace(0, 1, ""); return AstNode::prettyName(out); } string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const { string out; for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) { out += textp->text(); } if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, ""); if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, ""); if (out.substr(0, 1) == ".") out.replace(0, 1, ""); string::size_type pos; while ((pos = out.find('.')) != string::npos) { out.replace(pos, 1, "__"); } while ((pos = out.find("__DOT__")) != string::npos) { out.replace(pos, 7, "__"); } return out; } bool AstSenTree::hasClocked() const { UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { if (senp->isClocked()) return true; } return false; } bool AstSenTree::hasSettle() const { UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { if (senp->isSettle()) return true; } return false; } bool AstSenTree::hasInitial() const { UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { if (senp->isInitial()) return true; } return false; } bool AstSenTree::hasCombo() const { UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { if (senp->isCombo()) return true; } return false; } void AstTypeTable::clearCache() { // When we mass-change widthMin in V3WidthCommit, we need to correct the table. // Just clear out the maps; the search functions will be used to rebuild the map for (int i=0; i < static_cast(AstBasicDTypeKwd::_ENUM_MAX); ++i) { m_basicps[i] = NULL; } m_detailedMap.clear(); // Clear generic()'s so dead detection will work for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) { if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) { bdtypep->generic(false); } } } void AstTypeTable::repairCache() { // After we mass-change widthMin in V3WidthCommit, we need to correct the table. clearCache(); for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) { if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) { (void)findInsertSameDType(bdtypep); } } } AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { if (VL_UNLIKELY(!m_voidp)) { AstVoidDType* newp = new AstVoidDType(fl); addTypesp(newp); m_voidp = newp; } return m_voidp; } AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) { if (m_basicps[kwd]) return m_basicps[kwd]; // AstBasicDType* new1p = new AstBasicDType(fl, kwd); // Because the detailed map doesn't update this map, // check the detailed map for this same node // Also adds this new node to the detailed map AstBasicDType* newp = findInsertSameDType(new1p); if (newp != new1p) new1p->deleteTree(); else addTypesp(newp); // m_basicps[kwd] = newp; return newp; } AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width, int widthMin, AstNumeric numeric) { AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin); AstBasicDType* newp = findInsertSameDType(new1p); if (newp != new1p) new1p->deleteTree(); else addTypesp(newp); return newp; } AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, VNumRange range, int widthMin, AstNumeric numeric) { AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin); AstBasicDType* newp = findInsertSameDType(new1p); if (newp != new1p) new1p->deleteTree(); else addTypesp(newp); return newp; } AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) { VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(), nodep->nrange()); DetailedMap& mapr = m_detailedMap; DetailedMap::const_iterator it = mapr.find(key); if (it != mapr.end()) return it->second; mapr.insert(make_pair(key, nodep)); nodep->generic(true); // No addTypesp; the upper function that called new() is responsible for adding return nodep; } //====================================================================== // Special walking tree inserters void AstNode::addBeforeStmt(AstNode* newp, AstNode*) { UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt"); // Look up; virtual call will find where to put it this->backp()->addBeforeStmt(newp, this); } void AstNode::addNextStmt(AstNode* newp, AstNode*) { UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt"); // Look up; virtual call will find where to put it this->backp()->addNextStmt(newp, this); } void AstNodeStmt::addBeforeStmt(AstNode* newp, AstNode*) { // Insert newp before current node this->addHereThisAsNext(newp); } void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) { // Insert newp after current node this->addNextHere(newp); } void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) { // Special, as statements need to be put in different places // Belowp is how we came to recurse up to this point // Preconditions insert first just before themselves (the normal rule // for other statement types) if (belowp == precondsp()) { // Must have been first statement in precondsp list, so newp is new first statement belowp->addHereThisAsNext(newp); } else if (belowp == condp()) { // Goes before condition, IE in preconditions addPrecondsp(newp); } else if (belowp == bodysp()) { // Was first statement in body, so new front belowp->addHereThisAsNext(newp); } else { belowp->v3fatalSrc("Doesn't look like this was really under the while"); } } void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) { // Special, as statements need to be put in different places // Belowp is how we came to recurse up to this point // Preconditions insert first just before themselves (the normal rule // for other statement types) if (belowp == precondsp()) { // Next in precond list belowp->addNextHere(newp); } else if (belowp == condp()) { // Becomes first statement in body, body may have been empty if (bodysp()) { bodysp()->addHereThisAsNext(newp); } else { addBodysp(newp); } } else if (belowp == bodysp()) { // Next statement in body belowp->addNextHere(newp); } else { belowp->v3fatalSrc("Doesn't look like this was really under the while"); } } //====================================================================== // Per-type Debugging void AstNode::dump(std::ostream& str) const { str<m_backp <<" =editCountLast())?"#>":">") <<" {"<filenameLetters()<lastLineno()<<"}"; if (user1p()) str<<" u1="<dumpSmall(str); } } else { // V3Broken will throw an error if (dtypep()) str<<" %Error-dtype-exp=null,got="<AstNode::dump(str); if (keyword() != VAlwaysKwd::ALWAYS) str<<" ["<AstNode::dump(str); str<<" ["<AstNodeDType::dump(str); str<<" kwd="<AstNode::dump(str); str<<" sz"<AstNode::dump(str); if (recursive()) str<<" [RECURSIVE]"; if (modp()) { str<<" -> "; modp()->dump(str); } else { str<<" ->UNLINKED:"<AstNode::dump(str); str<<" -> "<AstNode::dump(str); if (immediate()) str<<" [IMMEDIATE]"; } void AstDisplay::dump(std::ostream& str) const { this->AstNode::dump(str); //str<<" "<AstNode::dump(str); str<<" -> "; if (itemp()) { itemp()->dump(str); } else { str<<"UNLINKED"; } } void AstIfaceRefDType::dump(std::ostream& str) const { this->AstNode::dump(str); if (cellName()!="") { str<<" cell="< "; cellp()->dump(str); } else if (ifacep()) { str<<" -> "; ifacep()->dump(str); } else { str<<" -> UNLINKED"; } } void AstIfaceRefDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"iface"; } void AstInitArray::dump(std::ostream& str) const { this->AstNode::dump(str); int n = 0; const AstInitArray::KeyItemMap& mapr = map(); for (AstInitArray::KeyItemMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) { if (n++ > 5) { str<<" ..."; break; } str<<" ["<first<<"]="<<(void*)it->second; } } void AstJumpGo::dump(std::ostream& str) const { this->AstNode::dump(str); str<<" -> "; if (labelp()) { labelp()->dump(str); } else { str<<"%Error:UNLINKED"; } } void AstMemberSel::dump(std::ostream& str) const { this->AstNode::dump(str); str << " -> "; if (varp()) { varp()->dump(str); } else { str << "%Error:UNLINKED"; } } void AstModportFTaskRef::dump(std::ostream& str) const { this->AstNode::dump(str); if (isExport()) str<<" EXPORT"; if (isImport()) str<<" IMPORT"; if (ftaskp()) { str<<" -> "; ftaskp()->dump(str); } else { str<<" -> UNLINKED"; } } void AstModportVarRef::dump(std::ostream& str) const { this->AstNode::dump(str); if (direction().isAny()) str<<" "< "; varp()->dump(str); } else { str<<" -> UNLINKED"; } } void AstPin::dump(std::ostream& str) const { this->AstNode::dump(str); if (modVarp()) { str<<" -> "; modVarp()->dump(str); } else { str<<" ->UNLINKED"; } if (svImplicit()) str<<" [.SV]"; } void AstTypedef::dump(std::ostream& str) const { this->AstNode::dump(str); if (attrPublic()) str<<" [PUBLIC]"; } void AstRange::dump(std::ostream& str) const { this->AstNode::dump(str); if (littleEndian()) str<<" [LITTLE]"; } void AstRefDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); if (defp()) { static bool s_recursing = false; if (!s_recursing) { // Prevent infinite dump if circular typedefs s_recursing = true; str<<" -> "; defp()->dump(str); s_recursing = false; } } else { str<<" -> UNLINKED"; } } void AstNodeUOrStructDType::dump(std::ostream& str) const { this->AstNode::dump(str); if (packed()) str<<" [PACKED]"; if (isFourstate()) str<<" [4STATE]"; } void AstNodeDType::dump(std::ostream& str) const { this->AstNode::dump(str); if (generic()) str<<" [GENERIC]"; if (AstNodeDType* dtp = virtRefDTypep()) { str<<" refdt="<dumpSmall(str); } } void AstNodeDType::dumpSmall(std::ostream& str) const { str<<"(" <<(generic()?"G/":"") <<((isSigned()&&!isDouble())?"s":"") <<(isNosign()?"n":"") <<(isDouble()?"d":"") <<(isString()?"str":""); if (!isDouble() && !isString()) { str<<"w"<<(widthSized()?"":"u")<AstNodeDType::dumpSmall(str); if (VN_IS(this, PackArrayDType)) str<<"p"; else str<<"u"; str<AstNodeDType::dump(str); str<<" "<AstNode::dump(str); str<<" L"<AstNode::dump(str); str<<" -> "<AstNode::dump(str); str<<" -> "<AstNode::dump(str); if (declRange().ranged()) { str<<" decl"<AstNode::dump(str); if (declRange().ranged()) { str<<" decl"<AstNode::dump(str); str<<" "; m_execMTaskp->dump(str); } void AstTypeTable::dump(std::ostream& str) const { this->AstNode::dump(str); for (int i=0; i < static_cast(AstBasicDTypeKwd::_ENUM_MAX); ++i) { if (AstBasicDType* subnodep = m_basicps[i]) { str< "; subnodep->dump(str); } } { const DetailedMap& mapr = m_detailedMap; for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) { AstBasicDType* dtypep = it->second; str< "; dtypep->dump(str); } } // Note get newline from caller too. } void AstAssocArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"[assoc-"<<(void*)keyDTypep()<<"]"; } void AstQueueDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"[queue]"; } void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"[]"; } void AstVoidDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"void"; } void AstVarScope::dump(std::ostream& str) const { this->AstNode::dump(str); if (isCircular()) str<<" [CIRC]"; if (varp()) { str<<" -> "; varp()->dump(str); } else { str<<" ->UNLINKED"; } } void AstVarXRef::dump(std::ostream& str) const { this->AstNode::dump(str); if (packagep()) { str<<" pkg="< "; else str<<" [RV] <- "; str<<".="<dump(str); } else if (varp()) { varp()->dump(str); } else { str<<"UNLINKED"; } } void AstVarRef::dump(std::ostream& str) const { this->AstNode::dump(str); if (packagep()) { str<<" pkg="< "; else str<<" [RV] <- "; if (varScopep()) { varScopep()->dump(str); } else if (varp()) { varp()->dump(str); } else { str<<"UNLINKED"; } } void AstVar::dump(std::ostream& str) const { this->AstNode::dump(str); if (isSc()) str<<" [SC]"; if (isPrimaryIO()) str<<(isInoutish()?" [PIO]":(isWritable()?" [PO]":" [PI]")); if (isIO()) str<<" "<AstNode::dump(str); if (isMulti()) str<<" [MULTI]"; } void AstSenItem::dump(std::ostream& str) const { this->AstNode::dump(str); str<<" ["<AstNode::dump(str); str<<" ["<AstNode::dump(str); if (packagep()) { str<<" pkg="< "; if (packagep()) { packagep()->dump(str); } else { str<<"UNLINKED"; } } void AstDot::dump(std::ostream& str) const { this->AstNode::dump(str); } void AstActive::dump(std::ostream& str) const { this->AstNode::dump(str); str<<" => "; if (sensesp()) { sensesp()->dump(str); } else { str<<"UNLINKED"; } } void AstNodeFTaskRef::dump(std::ostream& str) const { this->AstNode::dump(str); if (packagep()) { str<<" pkg="< "; if (dotted()!="") { str<<".="<dump(str); } else { str<<"UNLINKED"; } } void AstNodeFTask::dump(std::ostream& str) const { this->AstNode::dump(str); if (taskPublic()) str<<" [PUBLIC]"; if (prototype()) str<<" [PROTOTYPE]"; if (dpiImport()) str<<" [DPII]"; if (dpiExport()) str<<" [DPIX]"; if (dpiOpenChild()) str<<" [DPIOPENCHILD]"; if (dpiOpenParent()) str<<" [DPIOPENPARENT]"; if ((dpiImport() || dpiExport()) && cname()!=name()) str<<" [c="<AstNode::dump(str); if (unnamed()) str<<" [UNNAMED]"; if (generate()) str<<" [GEN]"; if (genforp()) str<<" [GENFOR]"; } void AstCoverDecl::dump(std::ostream& str) const { this->AstNode::dump(str); if (this->dataDeclNullp()) { str<<" -> "; this->dataDeclNullp()->dump(str); } else { if (binNum()) { str<<" bin"<AstNode::dump(str); str<<" -> "; if (declp()) { declp()->dump(str); } else { str<<"%Error:UNLINKED"; } } void AstTraceInc::dump(std::ostream& str) const { this->AstNode::dump(str); str<<" -> "; if (declp()) { declp()->dump(str); } else { str<<"%Error:UNLINKED"; } } void AstNodeText::dump(std::ostream& str) const { this->AstNode::dump(str); string out = text(); string::size_type pos; if ((pos = out.find('\n')) != string::npos) { out.erase(pos, out.length()-pos); out += "..."; } str<<" \""<AstNode::dump(str); } void AstCFile::dump(std::ostream& str) const { this->AstNode::dump(str); if (source()) str<<" [SRC]"; if (slow()) str<<" [SLOW]"; } void AstCCall::dump(std::ostream& str) const { this->AstNode::dump(str); if (funcp()) { str<<" "<name()<<" => "; funcp()->dump(str); } } void AstCFunc::dump(std::ostream& str) const { this->AstNode::dump(str); if (slow()) str<<" [SLOW]"; if (pure()) str<<" [PURE]"; if (isStatic().unknown()) str<<" [STATICU]"; else if (isStatic().trueU()) str<<" [STATIC]"; if (dpiImport()) str<<" [DPII]"; if (dpiExport()) str<<" [DPIX]"; if (dpiExportWrapper()) str<<" [DPIXWR]"; }