// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Add temporaries, such as for inst nodes // // 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. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* // V3Inst's Transformations: // // Each module: // Pins: // Create a wire assign to interconnect to submodule // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Global.h" #include "V3Inst.h" #include "V3Ast.h" #include "V3Const.h" #include //###################################################################### // Inst state, as a visitor of each AstNode class InstVisitor final : public AstNVisitor { private: // NODE STATE // Cleared each Cell: // AstPin::user1p() -> bool. True if created assignment already AstUser1InUse m_inuser1; // STATE AstCell* m_cellp = nullptr; // Current cell // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstCell* nodep) override { UINFO(4, " CELL " << nodep << endl); m_cellp = nodep; // VV***** We reset user1p() on each cell!!! AstNode::user1ClearTree(); iterateChildren(nodep); m_cellp = nullptr; } virtual void visit(AstPin* nodep) override { // PIN(p,expr) -> ASSIGNW(VARXREF(p),expr) (if sub's input) // or ASSIGNW(expr,VARXREF(p)) (if sub's output) UINFO(4, " PIN " << nodep << endl); if (!nodep->user1()) { // Simplify it V3Inst::pinReconnectSimple(nodep, m_cellp, false); } if (!nodep->exprp()) return; // No-connect if (debug() >= 9) nodep->dumpTree(cout, " Pin_oldb: "); V3Inst::checkOutputShort(nodep); // Use user1p on the PIN to indicate we created an assign for this pin if (!nodep->user1SetOnce()) { // Make an ASSIGNW (expr, pin) AstNode* exprp = nodep->exprp()->cloneTree(false); UASSERT_OBJ(exprp->width() == nodep->modVarp()->width(), nodep, "Width mismatch, should have been handled in pinReconnectSimple"); if (nodep->modVarp()->isInoutish()) { nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator"); } else if (nodep->modVarp()->isWritable()) { AstNode* rhsp = new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), VAccess::READ); AstAssignW* assp = new AstAssignW(exprp->fileline(), exprp, rhsp); m_cellp->addNextHere(assp); } else if (nodep->modVarp()->isNonOutput()) { // Don't bother moving constants now, // we'll be pushing the const down to the cell soon enough. AstNode* assp = new AstAssignW(exprp->fileline(), new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), VAccess::WRITE), exprp); m_cellp->addNextHere(assp); if (debug() >= 9) assp->dumpTree(cout, " _new: "); } else if (nodep->modVarp()->isIfaceRef() || (VN_IS(nodep->modVarp()->subDTypep(), UnpackArrayDType) && VN_IS(VN_CAST(nodep->modVarp()->subDTypep(), UnpackArrayDType) ->subDTypep(), IfaceRefDType))) { // Create an AstAssignVarScope for Vars to Cells so we can // link with their scope later AstNode* lhsp = new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), VAccess::READ); const AstVarRef* refp = VN_CAST(exprp, VarRef); const AstVarXRef* xrefp = VN_CAST(exprp, VarXRef); UASSERT_OBJ(refp || xrefp, exprp, "Interfaces: Pin is not connected to a VarRef or VarXRef"); AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp); m_cellp->addNextHere(assp); } else { nodep->v3error("Assigned pin is neither input nor output"); } } // We're done with the pin VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } virtual void visit(AstUdpTable* nodep) override { if (!v3Global.opt.bboxUnsup()) { // If we support primitives, update V3Undriven to remove special case nodep->v3warn(E_UNSUPPORTED, "Unsupported: Verilog 1995 UDP Tables. " "Use --bbox-unsup to ignore tables."); } } // Save some time virtual void visit(AstNodeMath*) override {} virtual void visit(AstNodeAssign*) override {} virtual void visit(AstAlways*) override {} //-------------------- virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: // CONSTRUCTORS explicit InstVisitor(AstNetlist* nodep) { iterate(nodep); } virtual ~InstVisitor() override = default; }; //###################################################################### class InstDeModVarVisitor final : public AstNVisitor { // Expand all module variables, and save names for later reference private: // STATE typedef std::map VarNameMap; VarNameMap m_modVarNameMap; // Per module, name of cloned variables VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstVar* nodep) override { if (VN_IS(nodep->dtypep(), IfaceRefDType)) { UINFO(8, " dm-1-VAR " << nodep << endl); insert(nodep); } iterateChildren(nodep); } virtual void visit(AstNodeMath*) override {} // Accelerate virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: // METHODS void insert(AstVar* nodep) { UINFO(8, " dmINSERT " << nodep << endl); m_modVarNameMap.insert(make_pair(nodep->name(), nodep)); } AstVar* find(const string& name) { const auto it = m_modVarNameMap.find(name); if (it != m_modVarNameMap.end()) { return it->second; } else { return nullptr; } } void dump() { for (const auto& itr : m_modVarNameMap) { cout << "-namemap: " << itr.first << " -> " << itr.second << endl; } } // CONSTRUCTORS InstDeModVarVisitor() = default; virtual ~InstDeModVarVisitor() override = default; void main(AstNodeModule* nodep) { UINFO(8, " dmMODULE " << nodep << endl); m_modVarNameMap.clear(); iterate(nodep); } }; //###################################################################### class InstDeVisitor final : public AstNVisitor { // Find all cells with arrays, and convert to non-arrayed private: // STATE // Range for arrayed instantiations, nullptr for normal instantiations AstRange* m_cellRangep = nullptr; int m_instSelNum = 0; // Current instantiation count 0..N-1 InstDeModVarVisitor m_deModVars; // State of variables for current cell module VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstVar* nodep) override { if (VN_IS(nodep->dtypep(), UnpackArrayDType) && VN_IS(VN_CAST(nodep->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType)) { UINFO(8, " dv-vec-VAR " << nodep << endl); AstUnpackArrayDType* arrdtype = VN_CAST(nodep->dtypep(), UnpackArrayDType); AstNode* prevp = nullptr; for (int i = arrdtype->lsb(); i <= arrdtype->msb(); ++i) { string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__"; UINFO(8, "VAR name insert " << varNewName << " " << nodep << endl); if (!m_deModVars.find(varNewName)) { AstIfaceRefDType* ifaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false); arrdtype->addNextHere(ifaceRefp); ifaceRefp->cellp(nullptr); AstVar* varNewp = nodep->cloneTree(false); varNewp->name(varNewName); varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); varNewp->dtypep(ifaceRefp); m_deModVars.insert(varNewp); if (!prevp) { prevp = varNewp; } else { prevp->addNextHere(varNewp); } } } if (prevp) nodep->addNextHere(prevp); if (prevp && debug() == 9) { prevp->dumpTree(cout, "newintf: "); cout << endl; } } iterateChildren(nodep); } virtual void visit(AstCell* nodep) override { UINFO(4, " CELL " << nodep << endl); // Find submodule vars UASSERT_OBJ(nodep->modp(), nodep, "Unlinked"); m_deModVars.main(nodep->modp()); // if (nodep->rangep()) { m_cellRangep = nodep->rangep(); AstVar* ifaceVarp = VN_CAST(nodep->nextp(), Var); bool isIface = ifaceVarp && VN_IS(ifaceVarp->dtypep(), UnpackArrayDType) && VN_IS(VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType); // Make all of the required clones for (int i = 0; i < m_cellRangep->elementsConst(); i++) { m_instSelNum = m_cellRangep->littleEndian() ? (m_cellRangep->elementsConst() - 1 - i) : i; int instNum = m_cellRangep->lsbConst() + i; AstCell* newp = nodep->cloneTree(false); nodep->addNextHere(newp); // Remove ranging and fix name newp->rangep()->unlinkFrBack()->deleteTree(); // Somewhat illogically, we need to rename the original name of the cell too. // as that is the name users expect for dotting // The spec says we add [x], but that won't work in C... newp->name(newp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__"); newp->origName(newp->origName() + "__BRA__" + cvtToStr(instNum) + "__KET__"); UINFO(8, " CELL loop " << newp << endl); // If this AstCell is actually an interface instantiation, also clone the IfaceRef // within the same parent module as the cell if (isIface) { AstUnpackArrayDType* arrdtype = VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType); AstIfaceRefDType* origIfaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType); origIfaceRefp->cellp(nullptr); AstVar* varNewp = ifaceVarp->cloneTree(false); AstIfaceRefDType* ifaceRefp = origIfaceRefp->cloneTree(false); arrdtype->addNextHere(ifaceRefp); ifaceRefp->cellp(newp); ifaceRefp->cellName(newp->name()); varNewp->name(varNewp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__"); varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(instNum) + "__KET__"); varNewp->dtypep(ifaceRefp); newp->addNextHere(varNewp); if (debug() == 9) { varNewp->dumpTree(cout, "newintf: "); cout << endl; } } // Fixup pins iterateAndNextNull(newp->pinsp()); if (debug() == 9) { newp->dumpTree(cout, "newcell: "); cout << endl; } } // Done. Delete original m_cellRangep = nullptr; if (isIface) { ifaceVarp->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(ifaceVarp), ifaceVarp); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { m_cellRangep = nullptr; iterateChildren(nodep); } } virtual void visit(AstPin* nodep) override { // Any non-direct pins need reconnection with a part-select if (!nodep->exprp()) return; // No-connect if (m_cellRangep) { UINFO(4, " PIN " << nodep << endl); int pinwidth = nodep->modVarp()->width(); int expwidth = nodep->exprp()->width(); std::pair pinDim = nodep->modVarp()->dtypep()->dimensions(false); std::pair expDim = nodep->exprp()->dtypep()->dimensions(false); UINFO(4, " PINVAR " << nodep->modVarp() << endl); UINFO(4, " EXP " << nodep->exprp() << endl); UINFO(4, " pinwidth ew=" << expwidth << " pw=" << pinwidth << " ed=" << expDim.first << "," << expDim.second << " pd=" << pinDim.first << "," << pinDim.second << endl); if (expDim.first == pinDim.first && expDim.second == pinDim.second + 1) { // Connection to array, where array dimensions match the instant dimension AstRange* rangep = VN_CAST(nodep->exprp()->dtypep(), UnpackArrayDType)->rangep(); int arraySelNum = rangep->littleEndian() ? (rangep->elementsConst() - 1 - m_instSelNum) : m_instSelNum; AstNode* exprp = nodep->exprp()->unlinkFrBack(); exprp = new AstArraySel(exprp->fileline(), exprp, arraySelNum); nodep->exprp(exprp); } else if (expwidth == pinwidth) { // NOP: Arrayed instants: widths match so connect to each instance } else if (expwidth == pinwidth * m_cellRangep->elementsConst()) { // Arrayed instants: one bit for each of the instants (each // assign is 1 pinwidth wide) if (m_cellRangep->littleEndian()) { nodep->exprp()->v3warn( LITENDIAN, "Little endian cell range connecting to vector: MSB < LSB of cell range: " << m_cellRangep->lsbConst() << ":" << m_cellRangep->msbConst()); } AstNode* exprp = nodep->exprp()->unlinkFrBack(); bool inputPin = nodep->modVarp()->isNonOutput(); if (!inputPin && !VN_IS(exprp, VarRef) // V3Const will collapse the SEL with the one we're about to make && !VN_IS(exprp, Concat) && !VN_IS(exprp, Sel)) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: Per-bit array instantiations " "with output connections to non-wires."); // Note spec allows more complicated matches such as slices and such } exprp = new AstSel(exprp->fileline(), exprp, pinwidth * m_instSelNum, pinwidth); nodep->exprp(exprp); } else { nodep->v3fatalSrc("Width mismatch; V3Width should have errored out."); } } // end expanding ranged cell else if (AstArraySel* arrselp = VN_CAST(nodep->exprp(), ArraySel)) { if (AstUnpackArrayDType* arrp = VN_CAST(arrselp->lhsp()->dtypep(), UnpackArrayDType)) { if (!VN_IS(arrp->subDTypep(), IfaceRefDType)) return; // Interface pin attaches to one element of arrayed interface V3Const::constifyParamsEdit(arrselp->rhsp()); const AstConst* constp = VN_CAST(arrselp->rhsp(), Const); if (!constp) { nodep->v3warn( E_UNSUPPORTED, "Unsupported: Non-constant index when passing interface to module"); return; } string index = AstNode::encodeNumber(constp->toSInt()); if (VN_IS(arrselp->lhsp(), SliceSel)) arrselp->lhsp()->v3error("Unsupported: interface slices"); AstVarRef* varrefp = VN_CAST(arrselp->lhsp(), VarRef); UASSERT_OBJ(varrefp, arrselp, "No interface varref under array"); AstVarXRef* newp = new AstVarXRef(nodep->fileline(), varrefp->name() + "__BRA__" + index + "__KET__", "", VAccess::WRITE); newp->dtypep(nodep->modVarp()->dtypep()); newp->packagep(varrefp->packagep()); arrselp->addNextHere(newp); VL_DO_DANGLING(arrselp->unlinkFrBack()->deleteTree(), arrselp); } } else { AstVar* pinVarp = nodep->modVarp(); AstUnpackArrayDType* pinArrp = VN_CAST(pinVarp->dtypep(), UnpackArrayDType); if (!pinArrp || !VN_IS(pinArrp->subDTypep(), IfaceRefDType)) return; // Arrayed pin/var attaches to arrayed submodule lower port/var, expand it AstNode* prevp = nullptr; AstNode* prevPinp = nullptr; // Clone the var referenced by the pin, and clone each var referenced by the varref // Clone pin varp: for (int i = pinArrp->lsb(); i <= pinArrp->msb(); ++i) { string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; AstVar* varNewp = nullptr; // Only clone the var once for all usages of a given child module if (!pinVarp->backp()) { varNewp = m_deModVars.find(varNewName); } else { AstIfaceRefDType* ifaceRefp = VN_CAST(pinArrp->subDTypep(), IfaceRefDType); ifaceRefp->cellp(nullptr); varNewp = pinVarp->cloneTree(false); varNewp->name(varNewName); varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); varNewp->dtypep(ifaceRefp); m_deModVars.insert(varNewp); if (!prevp) { prevp = varNewp; } else { prevp->addNextHere(varNewp); } } if (!varNewp) { if (debug() >= 9) m_deModVars.dump(); // LCOV_EXCL_LINE nodep->v3fatalSrc("Module dearray failed for " << AstNode::prettyNameQ(varNewName)); } // But clone the pin for each module instance // Now also clone the pin itself and update its varref AstPin* newp = nodep->cloneTree(false); newp->modVarp(varNewp); newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__"); // And replace exprp with a new varxref int offset = 0; const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef); if (varrefp) { } else if (AstSliceSel* slicep = VN_CAST(newp->exprp(), SliceSel)) { varrefp = VN_CAST(slicep->fromp(), VarRef); UASSERT(VN_IS(slicep->rhsp(), Const), "Slices should be constant"); offset = VN_CAST(slicep->rhsp(), Const)->toSInt(); } if (!varrefp) { newp->exprp()->v3error("Unexpected connection to arrayed port"); } string newname = varrefp->name() + "__BRA__" + cvtToStr(i + offset) + "__KET__"; AstVarXRef* newVarXRefp = new AstVarXRef(nodep->fileline(), newname, "", VAccess::WRITE); newVarXRefp->varp(newp->modVarp()); newp->exprp()->unlinkFrBack()->deleteTree(); newp->exprp(newVarXRefp); if (!prevPinp) { prevPinp = newp; } else { prevPinp->addNextHere(newp); } } if (prevp) { pinVarp->replaceWith(prevp); pushDeletep(pinVarp); } // else pinVarp already unlinked when another instance did this step nodep->replaceWith(prevPinp); pushDeletep(nodep); } } //-------------------- virtual void visit(AstNodeMath*) override {} // Accelerate virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: // CONSTRUCTORS explicit InstDeVisitor(AstNetlist* nodep) { iterate(nodep); } virtual ~InstDeVisitor() override = default; }; //###################################################################### // Inst static function class InstStatic final { private: VL_DEBUG_FUNC; // Declare debug() InstStatic() = default; // Static class static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) { if (cmpWidthp->width() > rhsp->width()) { rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS(fl, rhsp)) : static_cast(new AstExtend(fl, rhsp))); // Need proper widthMin, which may differ from AstSel created above rhsp->dtypeFrom(cmpWidthp); } else if (cmpWidthp->width() < rhsp->width()) { rhsp = new AstSel(fl, rhsp, 0, cmpWidthp->width()); // Need proper widthMin, which may differ from AstSel created above rhsp->dtypeFrom(cmpWidthp); } // else don't change dtype, as might be e.g. array of something return rhsp; } public: static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTristate, bool alwaysCvt) { // If a pin connection is "simple" leave it as-is // Else create a intermediate wire to perform the interconnect // Return the new assignment, if one was made // Note this module calles cloneTree() via new AstVar AstVar* pinVarp = pinp->modVarp(); if (!pinp->exprp()) { // No-connect, perhaps promote based on `unconnected_drive, // otherwise done if (pinVarp->direction() == VDirection::INPUT && cellp->modp()->unconnectedDrive().isSetTrue()) { pinp->exprp(new AstConst(pinp->fileline(), AstConst::StringToParse(), "'1")); } else if (pinVarp->direction() == VDirection::INPUT && cellp->modp()->unconnectedDrive().isSetFalse()) { pinp->exprp(new AstConst(pinp->fileline(), AstConst::StringToParse(), "'0")); } else { return nullptr; } } AstVarRef* connectRefp = VN_CAST(pinp->exprp(), VarRef); AstVarXRef* connectXRefp = VN_CAST(pinp->exprp(), VarXRef); AstBasicDType* pinBasicp = VN_CAST(pinVarp->dtypep(), BasicDType); // Maybe nullptr AstBasicDType* connBasicp = nullptr; AstAssignW* assignp = nullptr; if (connectRefp) connBasicp = VN_CAST(connectRefp->varp()->dtypep(), BasicDType); // if (!alwaysCvt && connectRefp && connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep()) && !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types // Done. Same data type } else if (!alwaysCvt && connectRefp && connectRefp->varp()->isIfaceRef()) { // Done. Interface } else if (!alwaysCvt && connectXRefp && connectXRefp->varp() && connectXRefp->varp()->isIfaceRef()) { } else if (!alwaysCvt && connBasicp && pinBasicp && connBasicp->width() == pinBasicp->width() && connBasicp->lsb() == pinBasicp->lsb() && !connectRefp->varp() ->isSc() // Need the signal as a 'shell' to convert types && connBasicp->width() == pinVarp->width()) { // Done. One to one interconnect won't need a temporary variable. } else if (!alwaysCvt && !forTristate && VN_IS(pinp->exprp(), Const)) { // Done. Constant. } else { // Make a new temp wire // if (1 || debug() >= 9) { pinp->dumpTree(cout, "-in_pin:"); } V3Inst::checkOutputShort(pinp); AstNode* pinexprp = pinp->exprp()->unlinkFrBack(); string newvarname = (string(pinVarp->isWritable() ? "__Vcellout" : "__Vcellinp") // Prevent name conflict if both tri & non-tri add signals + (forTristate ? "t" : "") + "__" + cellp->name() + "__" + pinp->name()); AstVar* newvarp = new AstVar(pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp); // Important to add statement next to cell, in case there is a // generate with same named cell cellp->addNextHere(newvarp); if (pinVarp->isInoutish()) { pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be" " direct one-to-one connection (without any expression)"); } else if (pinVarp->isWritable()) { // See also V3Inst AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, VAccess::READ); UINFO(5, "pinRecon width " << pinVarp->width() << " >? " << rhsp->width() << " >? " << pinexprp->width() << endl); rhsp = extendOrSel(pinp->fileline(), rhsp, pinVarp); pinp->exprp(new AstVarRef(newvarp->fileline(), newvarp, VAccess::WRITE)); AstNode* rhsSelp = extendOrSel(pinp->fileline(), rhsp, pinexprp); assignp = new AstAssignW(pinp->fileline(), pinexprp, rhsSelp); } else { // V3 width should have range/extended to make the widths correct assignp = new AstAssignW(pinp->fileline(), new AstVarRef(pinp->fileline(), newvarp, VAccess::WRITE), pinexprp); pinp->exprp(new AstVarRef(pinexprp->fileline(), newvarp, VAccess::READ)); } if (assignp) cellp->addNextHere(assignp); // if (debug()) { pinp->dumpTree(cout, "- out:"); } // if (debug()) { assignp->dumpTree(cout, "- aout:"); } } return assignp; } }; //###################################################################### // Inst class functions AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTristate, bool alwaysCvt) { return InstStatic::pinReconnectSimple(pinp, cellp, forTristate, alwaysCvt); } void V3Inst::checkOutputShort(AstPin* nodep) { if (nodep->modVarp()->direction() == VDirection::OUTPUT) { if (VN_IS(nodep->exprp(), Const) || VN_IS(nodep->exprp(), Extend) || (VN_IS(nodep->exprp(), Concat) && (VN_IS(VN_CAST(nodep->exprp(), Concat)->lhsp(), Const)))) { // Uses v3warn for error, as might be found multiple times nodep->v3warn(E_PORTSHORT, "Output port is connected to a constant pin," " electrical short"); } } } //###################################################################### // Inst class visitor void V3Inst::instAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { InstVisitor visitor(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("inst", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Inst::dearrayAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { InstDeVisitor visitor(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("dearray", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); }