forked from github/verilator
Internals: Refactor V3Inline to simplify if(m_cellp) out. No functional change.
This commit is contained in:
parent
2d64077fd0
commit
81bf95763c
564
src/V3Inline.cpp
564
src/V3Inline.cpp
@ -42,285 +42,9 @@
|
|||||||
#include "V3Stats.h"
|
#include "V3Stats.h"
|
||||||
#include "V3Ast.h"
|
#include "V3Ast.h"
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Inline state, as a visitor of each AstNode
|
|
||||||
|
|
||||||
// CONFIG
|
// CONFIG
|
||||||
static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it
|
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 "<<nodep<<endl);
|
|
||||||
// Do CellInlines under this, but don't move them
|
|
||||||
nodep->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 "<<nodep<<endl);
|
|
||||||
UINFO(5," To MOD "<<m_modp<<endl);
|
|
||||||
++m_statCells;
|
|
||||||
|
|
||||||
// Before cloning simplify pin assignments
|
|
||||||
// Better off before, as if module has multiple instantiations
|
|
||||||
// we'll save work, and we can't call pinReconnectSimple in
|
|
||||||
// this loop as it clone()s itself.
|
|
||||||
for (AstPin* pinp = nodep->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 "<<pinp->modVarp()<<endl);
|
|
||||||
// Make new signal; even though we'll optimize the interconnect, we
|
|
||||||
// need an alias to trace correctly. If tracing is disabled, we'll
|
|
||||||
// delete it in later optimizations.
|
|
||||||
AstVar* pinOldVarp = pinp->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 "<<connectRefp<<endl);
|
|
||||||
UINFO(6," -to "<<pinNewVarp<<endl);
|
|
||||||
pinNewVarp->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: "<<nodep->user2p()->castNode()<<endl);
|
|
||||||
if (!exprconstp && !exprvarrefp) {
|
|
||||||
nodep->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: "<<exprvarrefp<<endl);
|
|
||||||
if (nodep->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," "<<nodep<<endl);
|
|
||||||
}
|
|
||||||
nodep->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," "<<nodep<<endl);
|
|
||||||
}
|
|
||||||
nodep->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
|
// Inline state, as a visitor of each AstNode
|
||||||
|
|
||||||
@ -336,7 +60,7 @@ private:
|
|||||||
AstUser3InUse m_inuser3;
|
AstUser3InUse m_inuser3;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
AstNodeModule* m_modp; // Current module
|
AstNodeModule* m_modp; // Flattened cell's containing module
|
||||||
int m_stmtCnt; // Statements in module
|
int m_stmtCnt; // Statements in module
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
@ -441,6 +165,292 @@ public:
|
|||||||
virtual ~InlineMarkVisitor() {}
|
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 "<<nodep<<endl);
|
||||||
|
// Do CellInlines under this, but don't move them
|
||||||
|
nodep->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: "<<nodep->user2p()->castNode()<<endl);
|
||||||
|
if (!exprconstp && !exprvarrefp) {
|
||||||
|
nodep->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: "<<exprvarrefp<<endl);
|
||||||
|
if (nodep->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," "<<nodep<<endl);
|
||||||
|
nodep->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," "<<nodep<<endl);
|
||||||
|
nodep->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 "<<nodep<<endl);
|
||||||
|
UINFO(5," To MOD "<<m_modp<<endl);
|
||||||
|
++m_statCells;
|
||||||
|
|
||||||
|
// Before cloning simplify pin assignments
|
||||||
|
// Better off before, as if module has multiple instantiations
|
||||||
|
// we'll save work, and we can't call pinReconnectSimple in
|
||||||
|
// this loop as it clone()s itself.
|
||||||
|
for (AstPin* pinp = nodep->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 "<<pinp->modVarp()<<endl);
|
||||||
|
// Make new signal; even though we'll optimize the interconnect, we
|
||||||
|
// need an alias to trace correctly. If tracing is disabled, we'll
|
||||||
|
// delete it in later optimizations.
|
||||||
|
AstVar* pinOldVarp = pinp->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 "<<connectRefp<<endl);
|
||||||
|
UINFO(6," -to "<<pinNewVarp<<endl);
|
||||||
|
pinNewVarp->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
|
// Inline class functions
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user