// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Rename scope references to module-local references // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2014 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. // //************************************************************************* // DESCOPE TRANSFORMATIONS: // All modules: // Each VARREF/FUNCCALL // Change varref name() to be relative to current module // Remove varScopep() // This allows for better V3Combine'ing. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include "V3Global.h" #include "V3Descope.h" #include "V3Ast.h" #include "V3EmitCBase.h" //###################################################################### class DescopeVisitor : public AstNVisitor { private: // NODE STATE // Cleared entire netlist // AstCFunc::user() // bool. Indicates processing completed AstUser1InUse m_inuser1; // TYPES typedef multimap FuncMmap; // STATE AstNodeModule* m_modp; // Current module AstScope* m_scopep; // Current scope bool m_needThis; // Add thisp to function FuncMmap m_modFuncs; // Name of public functions added // METHODS static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } string descopedName(AstScope* scopep, bool& hierThisr, AstVar* varp=NULL) { UASSERT(scopep, "Var/Func not scoped\n"); hierThisr = true; if (varp && varp->isFuncLocal()) { return ""; // Relative to function, not in this } else if (scopep == m_scopep && m_modp->isTop()) { //return ""; // Reference to scope we're in, no need to HIER-> it return "vlTOPp->"; } else if (scopep == m_scopep && !m_modp->isTop() && 0) { // We no longer thisp-> as still get ambiguation problems m_needThis = true; return "thisp->"; // this-> but with restricted aliasing } else if (scopep->aboveScopep() && scopep->aboveScopep()==m_scopep && 0 // DISABLED: GCC considers the pointers ambiguous, so goes ld/store crazy ) { // Reference to scope of cell directly under this module, can just "cell->" string name = scopep->name(); string::size_type pos; if ((pos = name.rfind(".")) != string::npos) { name.erase(0,pos+1); } hierThisr = false; return name+"->"; } else { // Reference to something else, use global variable UINFO(8," Descope "<name()<name()<aboveScopep()) { // Top return "vlTOPp->"; // == "vlSymsp->TOPp->", but GCC would suspect aliases } else { return scopep->nameVlSym()+"."; } } } void makePublicFuncWrappers() { // We recorded all public functions in m_modFuncs. // If for any given name only one function exists, we can use that function directly. // If multiple functions exist, we need to select the appropriate scope. for (FuncMmap::iterator it = m_modFuncs.begin(); it!=m_modFuncs.end(); ++it) { string name = it->first; AstCFunc* topFuncp = it->second; FuncMmap::iterator nextIt1 = it; ++nextIt1; bool moreOfSame1 = (nextIt1!=m_modFuncs.end() && nextIt1->first == name); if (moreOfSame1) { // Multiple functions under this name, need a wrapper function UINFO(6," Wrapping "<cloneTree(false); if (newfuncp->initsp()) newfuncp->initsp()->unlinkFrBackWithNext()->deleteTree(); if (newfuncp->stmtsp()) newfuncp->stmtsp()->unlinkFrBackWithNext()->deleteTree(); if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree(); newfuncp->name(name); newfuncp->isStatic(false); newfuncp->addInitsp( new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); newfuncp->addInitsp(new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); topFuncp->addNextHere(newfuncp); // In the body, call each function if it matches the given scope for (FuncMmap::iterator eachIt = it; eachIt!=m_modFuncs.end() && eachIt->first==name; ++eachIt) { it = eachIt; AstCFunc* funcp = eachIt->second; FuncMmap::iterator nextIt2 = eachIt; ++nextIt2; bool moreOfSame = (nextIt2!=m_modFuncs.end() && nextIt2->first == name); if (!funcp->scopep()) funcp->v3fatalSrc("Not scoped"); UINFO(6," Wrapping "<argTypes()<<" und "<argTypes()<declPrivate(true); AstNode* argsp = NULL; for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { if (portp->isIO() && !portp->isFuncReturn()) { argsp = argsp->addNextNull(new AstVarRef(portp->fileline(), portp, portp->isOutput())); } } } AstNode* returnp = new AstCReturn (funcp->fileline(), new AstCCall (funcp->fileline(), funcp, argsp)); if (moreOfSame) { AstIf* ifp = new AstIf (funcp->fileline(), new AstEq(funcp->fileline(), new AstCMath(funcp->fileline(), "this", 64), new AstCMath(funcp->fileline(), string("&(") +funcp->scopep()->nameVlSym() +")", 64)), returnp, NULL); newfuncp->addStmtsp(ifp); } else { newfuncp->addStmtsp(returnp); } } // Not really any way the user could do this, and we'd need to come up with some return value //newfuncp->addStmtsp(new AstDisplay (newfuncp->fileline(), AstDisplayType::DT_DISPLAY, // string("%%Error: ")+name+"() called with bad scope", NULL)); //newfuncp->addStmtsp(new AstStop (newfuncp->fileline())); if (debug()>=9) newfuncp->dumpTree(cout," newfunc: "); } else { // Only a single function under this name, we can simply rename it UINFO(6," Wrapping "<name(name); } } } // VISITORS virtual void visit(AstNodeModule* nodep, AstNUser*) { m_modp = nodep; m_modFuncs.clear(); nodep->iterateChildren(*this); makePublicFuncWrappers(); m_modp = NULL; } virtual void visit(AstScope* nodep, AstNUser*) { m_scopep = nodep; nodep->iterateChildren(*this); m_scopep = NULL; } virtual void visit(AstVarScope* nodep, AstNUser*) { // Delete the varscope when we're finished nodep->unlinkFrBack(); pushDeletep(nodep); } virtual void visit(AstNodeVarRef* nodep, AstNUser*) { nodep->iterateChildren(*this); // Convert the hierch name if (!m_scopep) nodep->v3fatalSrc("Node not under scope"); bool hierThis; nodep->hiername(descopedName(nodep->varScopep()->scopep(), hierThis/*ref*/, nodep->varScopep()->varp())); nodep->hierThis(hierThis); nodep->varScopep(NULL); } virtual void visit(AstCCall* nodep, AstNUser*) { //UINFO(9," "<iterateChildren(*this); // Convert the hierch name if (!m_scopep) nodep->v3fatalSrc("Node not under scope"); if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope"); bool hierThis; nodep->hiername(descopedName(nodep->funcp()->scopep(), hierThis/*ref*/)); // Can't do this, as we may have more calls later // nodep->funcp()->scopep(NULL); } virtual void visit(AstCFunc* nodep, AstNUser*) { if (!nodep->user1()) { m_needThis = false; nodep->iterateChildren(*this); nodep->user1(true); if (m_needThis) { nodep->v3fatalSrc("old code"); // Really we should have more node types for backend optimization of this stuff string text = v3Global.opt.modPrefix() + "_" + m_modp->name() +"* thisp = &("+m_scopep->nameVlSym()+");\n"; nodep->addInitsp(new AstCStmt(nodep->fileline(), text)); } // If it's under a scope, move it up to the top if (m_scopep) { nodep->unlinkFrBack(); m_modp->addStmtp(nodep); if (nodep->funcPublic()) { // There may be multiple public functions by the same name; // record for later correction or making of shells m_modFuncs.insert(make_pair(nodep->name(), nodep)); nodep->name(m_scopep->nameDotless() +"__" + nodep->name()); } } } } virtual void visit(AstVar*, AstNUser*) {} virtual void visit(AstNode* nodep, AstNUser*) { nodep->iterateChildren(*this); } public: // CONSTRUCTORS DescopeVisitor(AstNetlist* nodep) { m_modp = NULL; m_scopep = NULL; m_needThis = false; nodep->accept(*this); } virtual ~DescopeVisitor() {} }; //###################################################################### // Descope class functions void V3Descope::descopeAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<