// $Id$ //************************************************************************* // DESCRIPTION: Verilator: Substitute constants and expressions in expr temp's // // Code available from: http://www.veripool.com/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2004-2006 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // General Public License or the Perl Artistic License. // // 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. // //************************************************************************* // V3Subst's Transformations: // // Each module: // Search all ASSIGN(WORDSEL(...)) and build what it's assigned to // Later usages of that word may then be replaced // //************************************************************************* #include "config.h" #include #include #include #include #include #include "V3Global.h" #include "V3Subst.h" #include "V3Const.h" #include "V3Stats.h" #include "V3Ast.h" //###################################################################### // Common debugging baseclass class SubstBaseVisitor : public AstNVisitor { public: static int debug() { return 0; } // static int debug() { return 9; } }; //###################################################################### // Subst state, as a visitor of each AstNode class SubstVarEntry { AstVar* m_varp; // Variable this tracks bool m_complexAssign;// True if assigned multiple times or in complex mannor AstNodeAssign* m_wholeAssignp; // Last assignment to entire array bool m_wholeUse; // True if used as entire wide array bool m_wordAssign; // True if any word assignments bool m_wordUse; // True if any individual word usage vector m_wordAssignps; // Last assignment to each word of this var vector m_wordUses; // True if each word was consumed vector m_wordComplex; // True if each word is complex int debug() { return SubstBaseVisitor::debug(); } public: SubstVarEntry (AstVar* varp) { // Construction for when a var is used m_varp = varp; m_complexAssign = false; m_wholeAssignp = NULL; m_wholeUse = false; m_wordAssign = false; m_wordUse = false; m_wordAssignps.resize(varp->widthWords()); m_wordUses.resize(varp->widthWords()); m_wordComplex.resize(varp->widthWords()); for (int i=0; iwidthWords(); i++) { m_wordAssignps[i] = NULL; m_wordUses[i] = false; m_wordComplex[i] = false; } } bool wordNumOk(int word) { return word < m_varp->widthWords(); } AstNodeAssign* getWordAssignp(int word) { if (!wordNumOk(word)) return NULL; else return m_wordAssignps[word]; } void assignWhole (AstNodeAssign* assp) { if (m_wholeAssignp) m_complexAssign = true; m_wholeAssignp = assp; } void assignWord (int word, AstNodeAssign* assp) { if (!wordNumOk(word) || getWordAssignp(word) || m_wordComplex[word]) m_complexAssign = true; m_wordAssign = true; if (wordNumOk(word)) m_wordAssignps[word] = assp; } void assignWordComplex (int word) { if (!wordNumOk(word) || getWordAssignp(word) || m_wordComplex[word]) m_complexAssign = true; m_wordComplex[word] = true; } void assignComplex() { m_complexAssign = true; } void consumeWhole() { //==consumeComplex as we don't know the difference m_wholeUse = true; } void consumeWord(int word) { m_wordUses[word] = true; m_wordUse = true; } // ACCESSORS AstNode* substWhole(AstNode* errp) { if (!m_varp->isWide() && !m_complexAssign && m_wholeAssignp && !m_wordAssign) { AstNodeAssign* assp = m_wholeAssignp; if (!assp) errp->v3fatalSrc("Reading whole that was never assigned"); return (assp->rhsp()); } else { return NULL; } } AstNode* substWord(AstNode* errp, int word) { // Return what to substitute given word number for if (!m_complexAssign && !m_wholeAssignp && !m_wordComplex[word]) { AstNodeAssign* assp = getWordAssignp(word); if (!assp) errp->v3fatalSrc("Reading a word that was never assigned, or bad word #"); return (assp->rhsp()); } else { return NULL; } } void deleteAssign (AstNodeAssign* nodep) { UINFO(5, "Delete "<unlinkFrBack()->deleteTree(); nodep=NULL; } void deleteUnusedAssign() { // If there are unused assignments in this var, kill them if (!m_wholeUse && !m_wordUse && m_wholeAssignp) { deleteAssign (m_wholeAssignp); m_wholeAssignp=NULL; } for (unsigned i=0; i SubstVar* for usage var, 0=not set yet // // STATE vector m_entryps; // Nodes to delete when we are finished int m_ops; // Number of operators on assign rhs V3Double0 m_statSubsts; // Statistic tracking enum { SUBST_MAX_OPS_SUBST = 30, // Maximum number of ops to substitute in SUBST_MAX_OPS_NA = 9999 }; // Not allowed to substitute // METHODS SubstVarEntry* getEntryp(AstVarRef* nodep) { if (!nodep->varp()->userp()) { SubstVarEntry* entryp = new SubstVarEntry (nodep->varp()); m_entryps.push_back(entryp); nodep->varp()->userp(entryp); return entryp; } else { SubstVarEntry* entryp = (SubstVarEntry*)(nodep->varp()->userp()); return entryp; } } // VISITORS virtual void visit(AstNodeAssign* nodep, AstNUser*) { m_ops = 0; nodep->rhsp()->iterateAndNext(*this); bool hit=false; if (AstVarRef* varrefp = nodep->lhsp()->castVarRef()) { if (varrefp->varp()->isStatementTemp()) { SubstVarEntry* entryp = getEntryp(varrefp); hit = true; if (m_ops > SUBST_MAX_OPS_SUBST) { UINFO(8," ASSIGNtooDeep "<assignComplex(); } else { UINFO(8," ASSIGNwhole "<assignWhole(nodep); } } } else if (AstWordSel* wordp = nodep->lhsp()->castWordSel()) { if (AstVarRef* varrefp = wordp->lhsp()->castVarRef()) { if (wordp->rhsp()->castConst() && varrefp->varp()->isStatementTemp()) { int word = wordp->rhsp()->castConst()->asInt(); SubstVarEntry* entryp = getEntryp(varrefp); hit = true; if (m_ops > SUBST_MAX_OPS_SUBST) { UINFO(8," ASSIGNtooDeep "<assignWordComplex(word); } else { UINFO(8," ASSIGNword"<assignWord(word, nodep); } } } } if (!hit) { nodep->lhsp()->accept(*this); } } void replaceSubstEtc(AstNode* nodep, AstNode* substp) { if (debug()>5) nodep->dumpTree(cout," substw_old: "); AstNode* newp = substp->cloneTree(true); if (!nodep->isQuad() && newp->isQuad()) { newp = new AstCast (newp->fileline(), newp, nodep); } if (debug()>5) newp->dumpTree(cout," w_new: "); nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; m_statSubsts++; } virtual void visit(AstWordSel* nodep, AstNUser*) { nodep->rhsp()->accept(*this); AstVarRef* varrefp = nodep->lhsp()->castVarRef(); AstConst* constp = nodep->rhsp()->castConst(); if (varrefp && varrefp->varp()->isStatementTemp() && !varrefp->lvalue() && constp) { // Nicely formed lvalues handled in NodeAssign // Other lvalues handled as unknown mess in AstVarRef int word = constp->asInt(); UINFO(8," USEword"<substWord (nodep, word)) { replaceSubstEtc(nodep, substp); nodep=NULL; } else { entryp->consumeWord(word); } } else { nodep->lhsp()->accept(*this); } } virtual void visit(AstVarRef* nodep, AstNUser*) { if (nodep->varp()->isStatementTemp()) { SubstVarEntry* entryp = getEntryp (nodep); if (nodep->lvalue()) { UINFO(8," ASSIGNcpx "<assignComplex(); } else if (AstNode* substp = entryp->substWhole(nodep)) { UINFO(8," USEwhole "<consumeWhole(); } } } virtual void visit(AstVar* nodep, AstNUser*) {} virtual void visit(AstNode* nodep, AstNUser*) { m_ops++; if (!nodep->isSubstOptimizable()) { m_ops = SUBST_MAX_OPS_NA; } nodep->iterateChildren(*this); } public: // CONSTUCTORS SubstVisitor(AstNode* nodep) { AstNode::userClearTree(); // userp() used on entire tree m_ops = 0; nodep->accept(*this); } virtual ~SubstVisitor() { V3Stats::addStat("Optimizations, Substituted temps", m_statSubsts); for (vector::iterator it = m_entryps.begin(); it != m_entryps.end(); ++it) { (*it)->deleteUnusedAssign(); delete (*it); } } }; //###################################################################### // Subst class functions void V3Subst::substituteAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<