From ca4e3c173753067893e833eea8736504fcf09fef Mon Sep 17 00:00:00 2001
From: Wilson Snyder <wsnyder@wsnyder.org>
Date: Tue, 26 Sep 2006 17:05:08 +0000
Subject: [PATCH] Eliminate assigned only variables

git-svn-id: file://localhost/svn/verilator/trunk/verilator@797 77ca24e4-aefa-0310-84f0-b9a241c72d87
---
 src/V3Dead.cpp    | 40 +++++++++++++++++++++++++++++++++++++---
 src/V3Life.cpp    | 26 +++++++++++++++++++++++---
 src/Verilator.cpp |  7 +++++++
 3 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp
index 573b16113..fb8cde3be 100644
--- a/src/V3Dead.cpp
+++ b/src/V3Dead.cpp
@@ -29,6 +29,7 @@
 #include <stdarg.h>
 #include <unistd.h>
 #include <vector>
+#include <map>
 
 #include "V3Global.h"
 #include "V3Dead.h"
@@ -70,10 +71,15 @@ private:
     //  AstVar::user()		-> int. Count of number of references
     //  AstVarScope::user()	-> int. Count of number of references
 
+    // TYPES
+    typedef multimap<AstVarScope*,AstNodeAssign*>	AssignMap;
+
     // STATE
     vector<AstVar*>		m_varsp;	// List of all encountered to avoid another loop through tree
     vector<AstVarScope*>	m_vscsp;	// List of all encountered to avoid another loop through tree
+    AssignMap			m_assignMap;	// List of all simple assignments for each variable
     bool			m_elimUserVars;	// Allow removal of user's vars
+    bool			m_sideEffect;	// Side effects discovered in assign RHS
     //int debug() { return 9; }
 
     // METHODS
@@ -102,6 +108,25 @@ private:
 	m_varsp.push_back(nodep);
     }
 
+    virtual void visit(AstNodeAssign* nodep, AstNUser*) {
+	// See if simple assignments to variables may be eliminated because that variable is never used.
+	// Similar code in V3Life
+	m_sideEffect = false;
+	nodep->rhsp()->iterateAndNext(*this);
+	// Has to be direct assignment without any EXTRACTing.
+	AstVarRef* varrefp = nodep->lhsp()->castVarRef();
+	if (varrefp && !m_sideEffect
+	    && varrefp->varScopep()) {	// For simplicity, we only remove post-scoping
+	    m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
+	} else {  // Track like any other statement
+	    nodep->lhsp()->iterateAndNext(*this);
+	}
+    }
+    virtual void visit(AstUCFunc* nodep, AstNUser*) {
+	m_sideEffect = true;  // If appears on assign RHS, don't ever delete the assignment
+	nodep->iterateChildren(*this);
+    }
+
     //-----
     virtual void visit(AstNode* nodep, AstNUser*) {
 	nodep->iterateChildren(*this);
@@ -131,14 +156,22 @@ private:
     }
     bool canElim(AstVar* nodep) {
 	return (!nodep->isSigPublic()	// Can't elim publics!
+		&& !nodep->isIO()
 		&& (nodep->isTemp() || nodep->isParam() || m_elimUserVars));
     }
     void deadCheckVar() {
 	// Delete any unused varscopes
 	for (vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
-	    if ((*it)->user() == 0 && canElim((*it)->varp())) {
-		UINFO(4,"  Dead "<<(*it)<<endl);
-		(*it)->unlinkFrBack()->deleteTree(); (*it)=NULL;
+	    AstVarScope* vscp = *it;
+	    if (vscp->user() == 0 && canElim(vscp->varp())) {
+		UINFO(4,"  Dead "<<vscp<<endl);
+		pair <AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
+		for (AssignMap::iterator it = eqrange.first; it != eqrange.second; ++it) {
+		    AstNodeAssign* assp = it->second;
+		    UINFO(4,"    Dead assign "<<assp<<endl);
+		    assp->unlinkFrBack()->deleteTree(); assp=NULL;
+		}
+		vscp->unlinkFrBack()->deleteTree(); vscp=NULL;
 	    }
 	}
 	for (vector<AstVar*>::iterator it = m_varsp.begin(); it!=m_varsp.end(); ++it) {
@@ -153,6 +186,7 @@ public:
     // CONSTRUCTORS
     DeadVisitor(AstNetlist* nodep, bool elimUserVars) {
 	m_elimUserVars = elimUserVars;
+	m_sideEffect = false;
 	// Operate on whole netlist
 	AstNode::userClearTree();	// userp() used on entire tree
 	nodep->accept(*this);
diff --git a/src/V3Life.cpp b/src/V3Life.cpp
index d94df75fa..c35a87b1f 100644
--- a/src/V3Life.cpp
+++ b/src/V3Life.cpp
@@ -264,7 +264,8 @@ public:
 class LifeVisitor : public AstNVisitor {
 private:
     // STATE
-    LifeState* m_statep;	// Current state
+    LifeState*	m_statep;	// Current state
+    bool	m_sideEffect;	// Side effects discovered in assign RHS
     //static int debug() { return 9; }
 
     // LIFE MAP
@@ -289,7 +290,9 @@ private:
     }
     virtual void visit(AstNodeAssign* nodep, AstNUser*) {
 	// Collect any used variables first, as lhs may also be on rhs
+	// Similar code in V3Dead
 	vluint64_t lastEdit = AstNode::editCountGbl();	// When it was last edited
+	m_sideEffect = false;
 	nodep->rhsp()->iterateAndNext(*this);
 	if (lastEdit != AstNode::editCountGbl()) {
 	    // We changed something, try to constant propagate, but don't delete the
@@ -297,7 +300,7 @@ private:
 	    V3Const::constifyTree(nodep->rhsp());
 	}
 	// Has to be direct assignment without any EXTRACTing.
-	if (nodep->lhsp()->castVarRef()) {
+	if (nodep->lhsp()->castVarRef() && !m_sideEffect) {
 	    AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
 	    if (!vscp) vscp->v3fatalSrc("Scope lost on variable");
 	    m_lifep->simpleAssign(vscp, nodep);
@@ -373,6 +376,10 @@ private:
 	// Enter the function and trace it
 	nodep->funcp()->accept(*this);
     }
+    virtual void visit(AstUCFunc* nodep, AstNUser*) {
+	m_sideEffect = true;  // If appears on assign RHS, don't ever delete the assignment
+	nodep->iterateChildren(*this);
+    }
 
     virtual void visit(AstVar*, AstNUser*) {}	// Don't want varrefs under it
     virtual void visit(AstNode* nodep, AstNUser*) {
@@ -381,9 +388,10 @@ private:
 
 public:
     // CONSTRUCTORS
-    LifeVisitor(AstCFunc* nodep, LifeState* statep) {
+    LifeVisitor(AstNode* nodep, LifeState* statep) {
 	UINFO(4,"  LifeVisitor on "<<nodep<<endl);
 	m_statep = statep;
+	m_sideEffect = false;
 	{
 	    m_lifep = new LifeBlock (NULL, m_statep);
 	    nodep->accept(*this);
@@ -409,6 +417,18 @@ private:
 	    LifeVisitor visitor (nodep, m_statep);
 	}
     }
+    virtual void visit(AstAlways* nodep, AstNUser*) {
+	// Usage model 2: Cleanup basic blocks
+	LifeVisitor visitor (nodep, m_statep);
+    }
+    virtual void visit(AstInitial* nodep, AstNUser*) {
+	// Usage model 2: Cleanup basic blocks
+	LifeVisitor visitor (nodep, m_statep);
+    }
+    virtual void visit(AstFinal* nodep, AstNUser*) {
+	// Usage model 2: Cleanup basic blocks
+	LifeVisitor visitor (nodep, m_statep);
+    }
     virtual void visit(AstVar*, AstNUser*) {}		// Accelerate
     virtual void visit(AstNodeStmt*, AstNUser*) {}	// Accelerate
     virtual void visit(AstNodeMath*, AstNUser*) {}	// Accelerate
diff --git a/src/Verilator.cpp b/src/Verilator.cpp
index e132efd92..85b95e5bc 100644
--- a/src/Verilator.cpp
+++ b/src/Verilator.cpp
@@ -248,7 +248,13 @@ void process () {
     V3Case::caseAll(v3Global.rootp());
     v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("case.tree"));
 
+    // Push constants across variables and remove redundant assignments
     V3Const::constifyAll(v3Global.rootp());
+    if (v3Global.opt.oLife()) {
+	V3Life::lifeAll(v3Global.rootp());
+	v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("life.tree"));
+    }
+
     //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
 
     // Make large low-fanin logic blocks into lookup tables
@@ -329,6 +335,7 @@ void process () {
     // Cleanup any dly vars or other temps that are simple assignments
     // Life must be done before Subst, as it assumes each CFunc under _eval is called only once.
     if (v3Global.opt.oLife()) {
+	V3Const::constifyAll(v3Global.rootp());
 	V3Life::lifeAll(v3Global.rootp());
     }
     if (v3Global.opt.oLifePost()) {