diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index e60221dc3..1f95de45b 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -42,285 +42,9 @@ #include "V3Stats.h" #include "V3Ast.h" -//###################################################################### -// Inline state, as a visitor of each AstNode - // CONFIG static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it -class InlineVisitor : public AstNVisitor { -private: - // NODE STATE - // Cleared entire netlist - // Input: - // AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor) - // Cleared each cell - // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to - // AstVar::user3() // bool Don't alias the user4, keep it as signal - - // STATE - AstNodeModule* m_modp; // Current module - AstCell* m_cellp; // Cell being cloned - V3Double0 m_statCells; // Statistic tracking - - static int debug() { - static int level = -1; - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); - return level; - } - - // VISITORS - virtual void visit(AstNetlist* nodep, AstNUser*) { - // Iterate modules backwards, in bottom-up order. Required! - nodep->iterateChildrenBackwards(*this); - } - virtual void visit(AstNodeModule* nodep, AstNUser*) { - if (m_cellp) { - } else { - m_modp = nodep; - } - nodep->iterateChildren(*this); - } - virtual void visit(AstCellInline* nodep, AstNUser*) { - // Inlined cell under the inline cell, need to move to avoid conflicts - if (m_cellp) { - nodep->unlinkFrBack(); - m_modp->addInlinesp(nodep); - // Rename - string name = m_cellp->name() + "__DOT__" + nodep->name(); - nodep->name(name); - UINFO(6, " Inline "<iterateChildren(*this); - } - } - virtual void visit(AstCell* nodep, AstNUser*) { - if (m_cellp) { - // Cell under the inline cell, need to rename to avoid conflicts - string name = m_cellp->name() + "__DOT__" + nodep->name(); - nodep->name(name); - nodep->iterateChildren(*this); - } - if (nodep->modp()->user1()) { // Marked with inline request - if (m_cellp) nodep->v3error("Cloning should have already been done bottom-up"); - UINFO(5," Inline CELL "<pinsp(); pinp; pinp=pinp->nextp()->castPin()) { - if (!pinp->exprp()) continue; - V3Inst::pinReconnectSimple(pinp, nodep, m_modp, false); - } - - // Clone original module - if (debug()>=9) { nodep->dumpTree(cout,"inlcell:"); } - //if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); } - AstNodeModule* newmodp = nodep->modp()->cloneTree(false); - if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); } - // Clear var markings - AstNode::user2ClearTree(); - // Create data for dotted variable resolution - AstCellInline* inlinep = new AstCellInline(nodep->fileline(), - nodep->name(), nodep->modp()->origName()); - m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells - // Create assignments to the pins - for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { - if (!pinp->exprp()) continue; - UINFO(6," Pin change from "<modVarp()<modVarp(); - AstVar* pinNewVarp = pinOldVarp->clonep()->castVar(); - - AstNode* connectRefp = pinp->exprp(); - if (!connectRefp->castConst() && !connectRefp->castVarRef()) { - pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); - } - if (pinNewVarp->isOutOnly() && connectRefp->castConst()) { - pinp->v3error("Output port is connected to a constant pin, electrical short"); - } - - // Propagate any attributes across the interconnect - pinNewVarp->propagateAttrFrom(pinOldVarp); - if (connectRefp->castVarRef()) { - connectRefp->castVarRef()->varp()->propagateAttrFrom(pinOldVarp); - } - - // One to one interconnect won't make a temporary variable. - // This prevents creating a lot of extra wires for clock signals. - // It will become a tracing alias. - UINFO(6,"One-to-one "<user2p(connectRefp); - // Public output inside the cell must go via an assign rather than alias - // Else the public logic will set the alias, loosing the value to be propagated up - // (InOnly isn't a problem as the AssignAlias will create the assignment for us) - pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly()); - } - // Cleanup var names, etc, to not conflict - m_cellp = nodep; - newmodp->iterate(*this); // Not iterateAndNext because newmodp isn't linked; no back - m_cellp = NULL; - // Move statements to top module - if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); } - AstNode* stmtsp = newmodp->stmtsp(); - if (stmtsp) stmtsp->unlinkFrBackWithNext(); - if (stmtsp) m_modp->addStmtp(stmtsp); - // Remove the cell - newmodp->deleteTree(); newmodp=NULL; // Clear any leftover ports, etc - nodep->unlinkFrBack(); - pushDeletep(nodep); nodep = NULL; - if (debug()>=9) { m_modp->dumpTree(cout,"donemod:"); } - } - } - virtual void visit(AstVar* nodep, AstNUser*) { - if (m_cellp) { - if (nodep->user2p()) { - // Make an assignment, so we'll trace it properly - // user2p is either a const or a var. - AstConst* exprconstp = nodep->user2p()->castNode()->castConst(); - AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef(); - UINFO(8,"connectto: "<user2p()->castNode()<v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); - } - if (exprconstp) { - m_modp->addStmtp(new AstAssignW(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, true), - exprconstp->cloneTree(true))); - } else if (nodep->user3()) { - // Public variable at the lower module end - we need to make sure we propagate - // the logic changes up and down; if we aliased, we might remove the change detection - // on the output variable. - UINFO(9,"public pin assign: "<isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias"); - m_modp->addStmtp(new AstAssignW(nodep->fileline(), - new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true), - new AstVarRef(nodep->fileline(), nodep, false))); - } else { - m_modp->addStmtp(new AstAssignAlias(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, true), - new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false))); - AstNode* nodebp=exprvarrefp->varp(); - nodep ->fileline()->modifyStateInherit(nodebp->fileline()); - nodebp->fileline()->modifyStateInherit(nodep ->fileline()); - } - } - // Variable under the inline cell, need to rename to avoid conflicts - // Also clear I/O bits, as it is now local. - string name = m_cellp->name() + "__DOT__" + nodep->name(); - if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name); - if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); } - if (debug()>=9) { nodep->valuep()->dumpTree(cout,"varchangei:"); } - } - if (nodep) nodep->iterateChildren(*this); - } - virtual void visit(AstNodeFTask* nodep, AstNUser*) { - if (m_cellp) { - // Function under the inline cell, need to rename to avoid conflicts - nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); - } - nodep->iterateChildren(*this); - } - virtual void visit(AstTypedef* nodep, AstNUser*) { - if (m_cellp) { - // Typedef under the inline cell, need to rename to avoid conflicts - nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); - } - nodep->iterateChildren(*this); - } - virtual void visit(AstVarRef* nodep, AstNUser*) { - if (m_cellp) { - if (nodep->varp()->user2p() // It's being converted to an alias. - && !nodep->varp()->user3() - && !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made) - AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst(); - AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef(); - if (exprconstp) { - nodep->replaceWith(exprconstp->cloneTree(true)); - nodep->deleteTree(); nodep=NULL; - return; - } - else if (exprvarrefp) { - nodep->varp( exprvarrefp->varp() ); - } - else { - nodep->v3fatalSrc("Null connection?\n"); - } - } - nodep->name(nodep->varp()->name()); - } - nodep->iterateChildren(*this); - } - virtual void visit(AstVarXRef* nodep, AstNUser*) { - if (m_cellp) { - // Track what scope it was originally under so V3LinkDot can resolve it - string newname = m_cellp->name(); - if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); } - nodep->inlinedDots(newname); - UINFO(8," "<iterateChildren(*this); - } - virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { - if (m_cellp) { - // Track what scope it was originally under so V3LinkDot can resolve it - string newname = m_cellp->name(); - if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); } - nodep->inlinedDots(newname); - UINFO(8," "<iterateChildren(*this); - } - - // Not needed, as V3LinkDot doesn't care about typedefs - //virtual void visit(AstRefDType* nodep, AstNUser*) {} - - virtual void visit(AstScopeName* nodep, AstNUser*) { - // If there's a %m in the display text, we add a special node that will contain the name() - // Similar code in V3Begin - if (m_cellp) { - // To keep correct visual order, must add before other Text's - AstNode* afterp = nodep->scopeAttrp(); - if (afterp) afterp->unlinkFrBackWithNext(); - nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name())); - if (afterp) nodep->scopeAttrp(afterp); - } - nodep->iterateChildren(*this); - } - virtual void visit(AstCoverDecl* nodep, AstNUser*) { - // Fix path in coverage statements - if (m_cellp) { - nodep->hier(m_cellp->prettyName() - + (nodep->hier()!="" ? ".":"") - + nodep->hier()); - } - nodep->iterateChildren(*this); - } - - //-------------------- - // Default: Just iterate - virtual void visit(AstNode* nodep, AstNUser*) { - nodep->iterateChildren(*this); - } - -public: - // CONSTUCTORS - InlineVisitor(AstNode* nodep) { - m_cellp = NULL; - m_modp = NULL; - nodep->accept(*this); - } - virtual ~InlineVisitor() { - V3Stats::addStat("Optimizations, Inlined cells", m_statCells); - } -}; - //###################################################################### // Inline state, as a visitor of each AstNode @@ -336,7 +60,7 @@ private: AstUser3InUse m_inuser3; // STATE - AstNodeModule* m_modp; // Current module + AstNodeModule* m_modp; // Flattened cell's containing module int m_stmtCnt; // Statements in module // METHODS @@ -441,6 +165,292 @@ public: virtual ~InlineMarkVisitor() {} }; +//###################################################################### +// After cell is cloned, relink the new module's contents + +class InlineRelinkVisitor : public AstNVisitor { +private: + // NODE STATE + // Input: + // See InlineVisitor + + // STATE + AstNodeModule* m_modp; // Current module + AstCell* m_cellp; // Cell being cloned + + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + // VISITORS + virtual void visit(AstCellInline* nodep, AstNUser*) { + // Inlined cell under the inline cell, need to move to avoid conflicts + nodep->unlinkFrBack(); + m_modp->addInlinesp(nodep); + // Rename + string name = m_cellp->name() + "__DOT__" + nodep->name(); + nodep->name(name); + UINFO(6, " Inline "<iterateChildren(*this); + } + virtual void visit(AstCell* nodep, AstNUser*) { + // Cell under the inline cell, need to rename to avoid conflicts + string name = m_cellp->name() + "__DOT__" + nodep->name(); + nodep->name(name); + nodep->iterateChildren(*this); + } + virtual void visit(AstVar* nodep, AstNUser*) { + if (nodep->user2p()) { + // Make an assignment, so we'll trace it properly + // user2p is either a const or a var. + AstConst* exprconstp = nodep->user2p()->castNode()->castConst(); + AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef(); + UINFO(8,"connectto: "<user2p()->castNode()<v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); + } + if (exprconstp) { + m_modp->addStmtp(new AstAssignW(nodep->fileline(), + new AstVarRef(nodep->fileline(), nodep, true), + exprconstp->cloneTree(true))); + } else if (nodep->user3()) { + // Public variable at the lower module end - we need to make sure we propagate + // the logic changes up and down; if we aliased, we might remove the change detection + // on the output variable. + UINFO(9,"public pin assign: "<isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias"); + m_modp->addStmtp(new AstAssignW(nodep->fileline(), + new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true), + new AstVarRef(nodep->fileline(), nodep, false))); + } else { + // Due to inlining child's variable now within the same module, so a AstVarRef not AstVarXRef below + m_modp->addStmtp(new AstAssignAlias(nodep->fileline(), + new AstVarRef(nodep->fileline(), nodep, true), + new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false))); + AstNode* nodebp=exprvarrefp->varp(); + nodep ->fileline()->modifyStateInherit(nodebp->fileline()); + nodebp->fileline()->modifyStateInherit(nodep ->fileline()); + } + } + // Variable under the inline cell, need to rename to avoid conflicts + // Also clear I/O bits, as it is now local. + string name = m_cellp->name() + "__DOT__" + nodep->name(); + if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name); + if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); } + if (debug()>=9) { nodep->valuep()->dumpTree(cout,"varchangei:"); } + if (nodep) nodep->iterateChildren(*this); + } + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + // Function under the inline cell, need to rename to avoid conflicts + nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); + nodep->iterateChildren(*this); + } + virtual void visit(AstTypedef* nodep, AstNUser*) { + // Typedef under the inline cell, need to rename to avoid conflicts + nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); + nodep->iterateChildren(*this); + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->varp()->user2p() // It's being converted to an alias. + && !nodep->varp()->user3() + && !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made) + AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst(); + AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef(); + if (exprconstp) { + nodep->replaceWith(exprconstp->cloneTree(true)); + nodep->deleteTree(); nodep=NULL; + return; + } + else if (exprvarrefp) { + nodep->varp( exprvarrefp->varp() ); + } + else { + nodep->v3fatalSrc("Null connection?\n"); + } + } + nodep->name(nodep->varp()->name()); + nodep->iterateChildren(*this); + } + virtual void visit(AstVarXRef* nodep, AstNUser*) { + // Track what scope it was originally under so V3LinkDot can resolve it + string newname = m_cellp->name(); + if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); } + nodep->inlinedDots(newname); + UINFO(8," "<iterateChildren(*this); + } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { + // Track what scope it was originally under so V3LinkDot can resolve it + string newname = m_cellp->name(); + if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); } + nodep->inlinedDots(newname); + UINFO(8," "<iterateChildren(*this); + } + + // Not needed, as V3LinkDot doesn't care about typedefs + //virtual void visit(AstRefDType* nodep, AstNUser*) {} + + virtual void visit(AstScopeName* nodep, AstNUser*) { + // If there's a %m in the display text, we add a special node that will contain the name() + // Similar code in V3Begin + // To keep correct visual order, must add before other Text's + AstNode* afterp = nodep->scopeAttrp(); + if (afterp) afterp->unlinkFrBackWithNext(); + nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name())); + if (afterp) nodep->scopeAttrp(afterp); + nodep->iterateChildren(*this); + } + virtual void visit(AstCoverDecl* nodep, AstNUser*) { + // Fix path in coverage statements + nodep->hier(m_cellp->prettyName() + + (nodep->hier()!="" ? ".":"") + + nodep->hier()); + nodep->iterateChildren(*this); + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp) { + m_modp = oldModp; + m_cellp = cellp; + cloneModp->accept(*this); + } + virtual ~InlineRelinkVisitor() {} +}; + +//###################################################################### +// Inline state, as a visitor of each AstNode + +class InlineVisitor : public AstNVisitor { +private: + // NODE STATE + // Cleared entire netlist + // Input: + // AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor) + // Cleared each cell + // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to + // AstVar::user3() // bool Don't alias the user2, keep it as signal + + // STATE + AstNodeModule* m_modp; // Current module + V3Double0 m_statCells; // Statistic tracking + + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + // Iterate modules backwards, in bottom-up order. Required! + nodep->iterateChildrenBackwards(*this); + } + virtual void visit(AstNodeModule* nodep, AstNUser*) { + m_modp = nodep; + nodep->iterateChildren(*this); + } + virtual void visit(AstCell* nodep, AstNUser*) { + if (nodep->modp()->user1()) { // Marked with inline request + UINFO(5," Inline CELL "<pinsp(); pinp; pinp=pinp->nextp()->castPin()) { + if (!pinp->exprp()) continue; + V3Inst::pinReconnectSimple(pinp, nodep, m_modp, false); + } + + // Clone original module + if (debug()>=9) { nodep->dumpTree(cout,"inlcell:"); } + //if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); } + AstNodeModule* newmodp = nodep->modp()->cloneTree(false); + if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); } + // Clear var markings + AstNode::user2ClearTree(); + // Create data for dotted variable resolution + AstCellInline* inlinep = new AstCellInline(nodep->fileline(), + nodep->name(), nodep->modp()->origName()); + m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells + // Create assignments to the pins + for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { + if (!pinp->exprp()) continue; + UINFO(6," Pin change from "<modVarp()<modVarp(); + AstVar* pinNewVarp = pinOldVarp->clonep()->castVar(); + + AstNode* connectRefp = pinp->exprp(); + if (!connectRefp->castConst() && !connectRefp->castVarRef()) { + pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); + } + if (pinNewVarp->isOutOnly() && connectRefp->castConst()) { + pinp->v3error("Output port is connected to a constant pin, electrical short"); + } + + // Propagate any attributes across the interconnect + pinNewVarp->propagateAttrFrom(pinOldVarp); + if (connectRefp->castVarRef()) { + connectRefp->castVarRef()->varp()->propagateAttrFrom(pinOldVarp); + } + + // One to one interconnect won't make a temporary variable. + // This prevents creating a lot of extra wires for clock signals. + // It will become a tracing alias. + UINFO(6,"One-to-one "<user2p(connectRefp); + // Public output inside the cell must go via an assign rather than alias + // Else the public logic will set the alias, loosing the value to be propagated up + // (InOnly isn't a problem as the AssignAlias will create the assignment for us) + pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly()); + } + // Cleanup var names, etc, to not conflict + { InlineRelinkVisitor(newmodp, m_modp, nodep); } + // Move statements to top module + if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); } + AstNode* stmtsp = newmodp->stmtsp(); + if (stmtsp) stmtsp->unlinkFrBackWithNext(); + if (stmtsp) m_modp->addStmtp(stmtsp); + // Remove the cell + newmodp->deleteTree(); newmodp=NULL; // Clear any leftover ports, etc + nodep->unlinkFrBack(); + pushDeletep(nodep); nodep = NULL; + if (debug()>=9) { m_modp->dumpTree(cout,"donemod:"); } + } + } + + //-------------------- + virtual void visit(AstNodeMath* nodep, AstNUser*) {} // Accelerate + virtual void visit(AstNodeStmt* nodep, AstNUser*) {} // Accelerate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + InlineVisitor(AstNode* nodep) { + m_modp = NULL; + nodep->accept(*this); + } + virtual ~InlineVisitor() { + V3Stats::addStat("Optimizations, Inlined cells", m_statCells); + } +}; + //###################################################################### // Inline class functions