|
|
|
|
@ -38,152 +38,242 @@
|
|
|
|
|
#include "V3Life.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Life state, as a visitor of each AstNode
|
|
|
|
|
// Structure for global state
|
|
|
|
|
|
|
|
|
|
class LifeVarEntry {
|
|
|
|
|
AstNode* m_assignp; // Last assignment to this varscope
|
|
|
|
|
bool m_firstSet; // True if creation was a set (and thus block above may have a set that can be deleted
|
|
|
|
|
class LifeState {
|
|
|
|
|
// STATE
|
|
|
|
|
public:
|
|
|
|
|
LifeVarEntry() { // Construction for when a var is used
|
|
|
|
|
clearAssign();
|
|
|
|
|
m_firstSet = false;
|
|
|
|
|
V3Double0 m_statAssnDel; // Statistic tracking
|
|
|
|
|
V3Double0 m_statAssnCon; // Statistic tracking
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LifeState() {}
|
|
|
|
|
~LifeState() {
|
|
|
|
|
V3Stats::addStat("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
|
|
|
|
V3Stats::addStat("Optimizations, Lifetime constant prop", m_statAssnCon);
|
|
|
|
|
}
|
|
|
|
|
LifeVarEntry(AstNode* assp) { // Construction for when a var is set
|
|
|
|
|
m_assignp = assp;
|
|
|
|
|
m_firstSet = true;
|
|
|
|
|
}
|
|
|
|
|
LifeVarEntry(bool) { // Construction for when a var is set, but ass isn't
|
|
|
|
|
m_assignp = NULL;
|
|
|
|
|
m_firstSet = true;
|
|
|
|
|
}
|
|
|
|
|
AstNode* assignp() const { return m_assignp; }
|
|
|
|
|
bool firstSet() const { return m_firstSet; }
|
|
|
|
|
void clearAssign() { m_assignp = NULL; }
|
|
|
|
|
void newerAssign(AstNode* assp) { m_assignp = assp; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LifeVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstVarScope::user() -> int. Used in combining to detect duplicates
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Structure for each variable encountered
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
//static int debug() { return 9; }
|
|
|
|
|
int m_ifCount; // If counter
|
|
|
|
|
V3Double0 m_statAssnDel; // Statistic tracking
|
|
|
|
|
class LifeVarEntry {
|
|
|
|
|
AstNodeAssign* m_assignp; // Last assignment to this varscope, NULL if no longer relevant
|
|
|
|
|
AstConst* m_constp; // Known constant value
|
|
|
|
|
bool m_setBeforeUse; // First access was a set (and thus block above may have a set that can be deleted
|
|
|
|
|
bool m_everSet; // Was ever assigned (and thus above block may not preserve constant propagation)
|
|
|
|
|
|
|
|
|
|
inline void init (bool setBeforeUse) {
|
|
|
|
|
m_assignp = NULL;
|
|
|
|
|
m_constp = NULL;
|
|
|
|
|
m_setBeforeUse = setBeforeUse;
|
|
|
|
|
m_everSet = false;
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
class SIMPLEASSIGN {};
|
|
|
|
|
class COMPLEXASSIGN {};
|
|
|
|
|
class CONSUMED {};
|
|
|
|
|
|
|
|
|
|
LifeVarEntry(SIMPLEASSIGN, AstNodeAssign* assp) {
|
|
|
|
|
init(true); simpleAssign(assp);
|
|
|
|
|
}
|
|
|
|
|
LifeVarEntry(COMPLEXASSIGN) {
|
|
|
|
|
init(false); complexAssign();
|
|
|
|
|
}
|
|
|
|
|
LifeVarEntry(CONSUMED) {
|
|
|
|
|
init(false); consumed();
|
|
|
|
|
}
|
|
|
|
|
~LifeVarEntry() {}
|
|
|
|
|
inline void simpleAssign(AstNodeAssign* assp) { // New simple A=.... assignment
|
|
|
|
|
m_assignp = assp;
|
|
|
|
|
m_constp = NULL;
|
|
|
|
|
m_everSet = true;
|
|
|
|
|
if (assp->rhsp()->castConst()) m_constp = assp->rhsp()->castConst();
|
|
|
|
|
}
|
|
|
|
|
inline void complexAssign() { // A[x]=... or some complicated assignment
|
|
|
|
|
m_assignp = NULL;
|
|
|
|
|
m_constp = NULL;
|
|
|
|
|
m_everSet = true;
|
|
|
|
|
}
|
|
|
|
|
inline void consumed() { // Rvalue read of A
|
|
|
|
|
m_assignp = NULL;
|
|
|
|
|
}
|
|
|
|
|
AstNodeAssign* assignp() const { return m_assignp; }
|
|
|
|
|
AstConst* constNodep() const { return m_constp; }
|
|
|
|
|
bool setBeforeUse() const { return m_setBeforeUse; }
|
|
|
|
|
bool everSet() const { return m_everSet; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Structure for all variables under a given meta-basic block
|
|
|
|
|
|
|
|
|
|
class LifeBlock {
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared each AstIf:
|
|
|
|
|
// AstVarScope::user() -> int. Used in combining to detect duplicates
|
|
|
|
|
|
|
|
|
|
// LIFE MAP
|
|
|
|
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
|
|
|
|
typedef std::map<AstVarScope*, LifeVarEntry> LifeMap;
|
|
|
|
|
LifeMap *m_lifep; // Current active lifetime map for current scope
|
|
|
|
|
LifeMap m_map; // Current active lifetime map for current scope
|
|
|
|
|
LifeBlock* m_aboveLifep; // Upper life, or NULL
|
|
|
|
|
LifeState* m_statep; // Current global state
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LifeBlock(LifeBlock* aboveLifep, LifeState* statep) {
|
|
|
|
|
m_aboveLifep = aboveLifep; // Null if top
|
|
|
|
|
m_statep = statep;
|
|
|
|
|
}
|
|
|
|
|
~LifeBlock() {}
|
|
|
|
|
// METHODS
|
|
|
|
|
void checkRemoveAssign(LifeVarEntry* entp) {
|
|
|
|
|
// Check the var entry, and remove if appropriate
|
|
|
|
|
if (AstNode* oldassp = entp->assignp()) {
|
|
|
|
|
UINFO(7," PREV: "<<oldassp<<endl);
|
|
|
|
|
// Redundant assignment, in same level block
|
|
|
|
|
if (debug()>4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK ");
|
|
|
|
|
entp->clearAssign();
|
|
|
|
|
oldassp->unlinkFrBack();
|
|
|
|
|
pushDeletep(oldassp);
|
|
|
|
|
m_statAssnDel++;
|
|
|
|
|
void checkRemoveAssign(const LifeMap::iterator& it) {
|
|
|
|
|
AstVar* varp = it->first->varp();
|
|
|
|
|
LifeVarEntry* entp = &(it->second);
|
|
|
|
|
if (!varp->isSigPublic()) {
|
|
|
|
|
// Rather then track what sigs AstUCFunc/AstUCStmt may change,
|
|
|
|
|
// we just don't optimize any public sigs
|
|
|
|
|
// Check the var entry, and remove if appropriate
|
|
|
|
|
if (AstNode* oldassp = entp->assignp()) {
|
|
|
|
|
UINFO(7," PREV: "<<oldassp<<endl);
|
|
|
|
|
// Redundant assignment, in same level block
|
|
|
|
|
if (debug()>4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK ");
|
|
|
|
|
entp->complexAssign();
|
|
|
|
|
oldassp->unlinkFrBack()->deleteTree(); oldassp=NULL;
|
|
|
|
|
m_statep->m_statAssnDel++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void setLast(AstVarScope* nodep, AstNode* assp) {
|
|
|
|
|
void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) {
|
|
|
|
|
// Do we have a old assignment we can nuke?
|
|
|
|
|
UINFO(4," ASSIGNof: "<<nodep<<endl);
|
|
|
|
|
UINFO(7," new: "<<assp<<endl);
|
|
|
|
|
LifeMap::iterator iter = m_lifep->find(nodep);
|
|
|
|
|
if (iter != m_lifep->end()) {
|
|
|
|
|
checkRemoveAssign(&(iter->second));
|
|
|
|
|
iter->second.newerAssign(assp);
|
|
|
|
|
LifeMap::iterator it = m_map.find(nodep);
|
|
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
checkRemoveAssign(it);
|
|
|
|
|
it->second.simpleAssign(assp);
|
|
|
|
|
} else {
|
|
|
|
|
m_lifep->insert(make_pair(nodep,LifeVarEntry(assp)));
|
|
|
|
|
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::SIMPLEASSIGN(), assp)));
|
|
|
|
|
}
|
|
|
|
|
//lifeDump();
|
|
|
|
|
}
|
|
|
|
|
void clearLastAssign(AstVarScope* nodep) {
|
|
|
|
|
void complexAssign(AstVarScope* nodep) {
|
|
|
|
|
UINFO(4," clearof: "<<nodep<<endl);
|
|
|
|
|
LifeMap::iterator iter = m_lifep->find(nodep);
|
|
|
|
|
if (iter != m_lifep->end()) {
|
|
|
|
|
iter->second.clearAssign();
|
|
|
|
|
LifeMap::iterator it = m_map.find(nodep);
|
|
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
it->second.complexAssign();
|
|
|
|
|
} else {
|
|
|
|
|
m_lifep->insert(make_pair(nodep,LifeVarEntry()));
|
|
|
|
|
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::COMPLEXASSIGN())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void lifeDump(LifeMap* lifep) {
|
|
|
|
|
void varUsageReplace (AstVarScope* nodep, AstVarRef* varrefp) {
|
|
|
|
|
// Variable rvalue. If it references a constant, we can simply replace it
|
|
|
|
|
LifeMap::iterator it = m_map.find(nodep);
|
|
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
if (AstConst* constp = it->second.constNodep()) {
|
|
|
|
|
if (!varrefp->varp()->isSigPublic()) {
|
|
|
|
|
// Aha, variable is constant; substitute in.
|
|
|
|
|
// We'll later constant propagate
|
|
|
|
|
UINFO(4," replaceconst: "<<varrefp<<endl);
|
|
|
|
|
varrefp->replaceWith(constp->cloneTree(false));
|
|
|
|
|
varrefp->deleteTree(); varrefp=NULL;
|
|
|
|
|
m_statep->m_statAssnCon++;
|
|
|
|
|
return; // **DONE, no longer a var reference**
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UINFO(4," usage: "<<nodep<<endl);
|
|
|
|
|
it->second.consumed();
|
|
|
|
|
} else {
|
|
|
|
|
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::CONSUMED())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void complexAssignFind(AstVarScope* nodep) {
|
|
|
|
|
LifeMap::iterator it = m_map.find(nodep);
|
|
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
UINFO(4," casfind: "<<it->first<<endl);
|
|
|
|
|
it->second.complexAssign();
|
|
|
|
|
} else {
|
|
|
|
|
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::COMPLEXASSIGN())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void consumedFind(AstVarScope* nodep) {
|
|
|
|
|
LifeMap::iterator it = m_map.find(nodep);
|
|
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
it->second.consumed();
|
|
|
|
|
} else {
|
|
|
|
|
m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::CONSUMED())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void lifeToAbove() {
|
|
|
|
|
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
|
|
|
|
if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level");
|
|
|
|
|
for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) {
|
|
|
|
|
AstVarScope* nodep = it->first;
|
|
|
|
|
m_aboveLifep->complexAssignFind(nodep);
|
|
|
|
|
if (it->second.everSet()) {
|
|
|
|
|
// Record there may be an assignment, so we don't constant propagate across the if.
|
|
|
|
|
complexAssignFind(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
// Record consumption, so we don't eliminate earlier assignments
|
|
|
|
|
consumedFind(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void dualBranch (LifeBlock* life1p, LifeBlock* life2p) {
|
|
|
|
|
// Find any common sets on both branches of IF and propagate upwards
|
|
|
|
|
//life1p->lifeDump();
|
|
|
|
|
//life2p->lifeDump();
|
|
|
|
|
AstNode::userClearTree(); // userp() used on entire tree
|
|
|
|
|
for (LifeMap::iterator it = life1p->m_map.begin(); it!=life1p->m_map.end(); ++it) {
|
|
|
|
|
// When the if branch sets a var before it's used, mark that variable
|
|
|
|
|
if (it->second.setBeforeUse()) it->first->user(1);
|
|
|
|
|
}
|
|
|
|
|
for (LifeMap::iterator it = life2p->m_map.begin(); it!=life2p->m_map.end(); ++it) {
|
|
|
|
|
// When the else branch sets a var before it's used
|
|
|
|
|
AstVarScope* nodep = it->first;
|
|
|
|
|
if (it->second.setBeforeUse() && nodep->user()) {
|
|
|
|
|
// Both branches set the var, we can remove the assignment before the IF.
|
|
|
|
|
UINFO(4,"DUALBRANCH "<<nodep<<endl);
|
|
|
|
|
LifeMap::iterator itab = m_map.find(nodep);
|
|
|
|
|
if (itab != m_map.end()) {
|
|
|
|
|
checkRemoveAssign(itab);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//this->lifeDump();
|
|
|
|
|
}
|
|
|
|
|
// DEBUG
|
|
|
|
|
void lifeDump() {
|
|
|
|
|
UINFO(5, " LifeMap:"<<endl);
|
|
|
|
|
for (LifeMap::iterator it = lifep->begin(); it!=lifep->end(); ++it) {
|
|
|
|
|
for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) {
|
|
|
|
|
UINFO(5, " Ent: "
|
|
|
|
|
<<(it->second.firstSet()?"[F] ":" ")
|
|
|
|
|
<<(it->second.setBeforeUse()?"[F] ":" ")
|
|
|
|
|
<<it->first<<endl);
|
|
|
|
|
if (it->second.assignp()) {
|
|
|
|
|
UINFO(5, " Ass: "<<it->second.assignp()<<endl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void clearLifeFromSublife (LifeMap* sublifep) {
|
|
|
|
|
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
|
|
|
|
for (LifeMap::iterator it = sublifep->begin(); it!=sublifep->end(); ++it) {
|
|
|
|
|
clearLastAssign(it->first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void dualBranch (LifeMap* life1p, LifeMap* life2p) {
|
|
|
|
|
// Find any common sets on both branches of IF and propagate upwards
|
|
|
|
|
//lifeDump(life1p);
|
|
|
|
|
//lifeDump(life2p);
|
|
|
|
|
m_ifCount++;
|
|
|
|
|
for (LifeMap::iterator it = life1p->begin(); it!=life1p->end(); ++it) {
|
|
|
|
|
// When the if branch sets a var before it's used, mark that variable
|
|
|
|
|
if (it->second.firstSet()) it->first->user(m_ifCount);
|
|
|
|
|
}
|
|
|
|
|
for (LifeMap::iterator it = life2p->begin(); it!=life2p->end(); ++it) {
|
|
|
|
|
// When the else branch sets a var before it's used
|
|
|
|
|
AstVarScope* nodep = it->first;
|
|
|
|
|
if (it->second.firstSet()
|
|
|
|
|
&& nodep->user()==m_ifCount) { // And the if hit the same var
|
|
|
|
|
UINFO(4,"DUALBRANCH "<<nodep<<endl);
|
|
|
|
|
LifeMap::iterator iter = m_lifep->find(nodep);
|
|
|
|
|
if (iter != m_lifep->end()) {
|
|
|
|
|
checkRemoveAssign(&(iter->second));
|
|
|
|
|
iter->second.clearAssign();
|
|
|
|
|
} else {
|
|
|
|
|
m_lifep->insert(make_pair(nodep,LifeVarEntry(true)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//lifeDump(m_lifep);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Life state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class LifeVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
|
|
|
|
LifeState* m_statep; // Current state
|
|
|
|
|
//static int debug() { return 9; }
|
|
|
|
|
|
|
|
|
|
// LIFE MAP
|
|
|
|
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
|
|
|
|
typedef std::map<AstVarScope*, LifeVarEntry> LifeMap;
|
|
|
|
|
LifeBlock* m_lifep; // Current active lifetime map for current scope
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstModule* nodep, AstNUser*) {
|
|
|
|
|
// Only track the top scopes, not lower level functions
|
|
|
|
|
if (nodep->isTop()) nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstTopScope* nodep, AstNUser*) {
|
|
|
|
|
AstNode::userClearTree(); // userp() used on entire tree
|
|
|
|
|
AstNode::user2ClearTree(); // userp() used on entire tree
|
|
|
|
|
AstNode::user3ClearTree(); // userp() used on entire tree
|
|
|
|
|
AstNode::user4ClearTree(); // userp() used on entire tree
|
|
|
|
|
|
|
|
|
|
AstScope* scopep = nodep->scopep();
|
|
|
|
|
if (!scopep) nodep->v3fatalSrc("TopScope has no scope\n");
|
|
|
|
|
AstCFunc* evalp = NULL;
|
|
|
|
|
for (AstNode* searchp = scopep->blocksp(); searchp; searchp=searchp->nextp()) {
|
|
|
|
|
if (AstCFunc* funcp = searchp->castCFunc()) {
|
|
|
|
|
if (funcp->name() == "_eval") {
|
|
|
|
|
evalp = funcp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!evalp) nodep->v3fatalSrc("Top _eval entry function not found\n");
|
|
|
|
|
evalp->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
|
|
|
|
// Consumption/generation of a variable,
|
|
|
|
|
// it's used so can't elim assignment before this use.
|
|
|
|
|
@ -191,44 +281,59 @@ private:
|
|
|
|
|
//
|
|
|
|
|
AstVarScope* vscp = nodep->varScopep();
|
|
|
|
|
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
|
|
|
|
clearLastAssign(vscp);
|
|
|
|
|
if (nodep->lvalue()) {
|
|
|
|
|
m_lifep->complexAssign(vscp);
|
|
|
|
|
} else {
|
|
|
|
|
m_lifep->varUsageReplace(vscp, nodep); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
|
|
|
|
// Collect any used variables first, as lhs may also be on rhs
|
|
|
|
|
vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited
|
|
|
|
|
nodep->rhsp()->iterateAndNext(*this);
|
|
|
|
|
if (lastEdit != AstNode::editCountGbl()) {
|
|
|
|
|
// We changed something, try to constant propagate, but don't delete the
|
|
|
|
|
// assignment as we still need nodep to remain.
|
|
|
|
|
V3Const::constifyTree(nodep->rhsp());
|
|
|
|
|
}
|
|
|
|
|
// Has to be direct assignment without any EXTRACTing.
|
|
|
|
|
if (nodep->lhsp()->castVarRef()) {
|
|
|
|
|
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
|
|
|
|
|
if (!vscp) vscp->v3fatalSrc("Scope lost on variable");
|
|
|
|
|
setLast(vscp, nodep);
|
|
|
|
|
m_lifep->simpleAssign(vscp, nodep);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignDly* nodep, AstNUser*) {
|
|
|
|
|
// Don't treat as normal assign; V3Life doesn't understand time sense
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---- Track control flow changes
|
|
|
|
|
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
|
|
|
|
UINFO(4," IF "<<nodep<<endl);
|
|
|
|
|
// Condition is part of PREVIOUS block
|
|
|
|
|
nodep->condp()->iterateAndNext(*this);
|
|
|
|
|
LifeMap* prevLifeMapp = m_lifep;
|
|
|
|
|
LifeMap* ifLifep = new LifeMap;
|
|
|
|
|
LifeMap* elseLifep = new LifeMap;
|
|
|
|
|
LifeBlock* prevLifep = m_lifep;
|
|
|
|
|
LifeBlock* ifLifep = new LifeBlock (prevLifep, m_statep);
|
|
|
|
|
LifeBlock* elseLifep = new LifeBlock (prevLifep, m_statep);
|
|
|
|
|
{
|
|
|
|
|
m_lifep = ifLifep;
|
|
|
|
|
nodep->ifsp()->iterateAndNext(*this);
|
|
|
|
|
m_lifep = prevLifep;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
m_lifep = elseLifep;
|
|
|
|
|
nodep->elsesp()->iterateAndNext(*this);
|
|
|
|
|
m_lifep = prevLifep;
|
|
|
|
|
}
|
|
|
|
|
UINFO(4," join "<<endl);
|
|
|
|
|
m_lifep = prevLifeMapp;
|
|
|
|
|
// Find sets on both flows
|
|
|
|
|
dualBranch (ifLifep, elseLifep);
|
|
|
|
|
m_lifep->dualBranch (ifLifep, elseLifep);
|
|
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
clearLifeFromSublife(ifLifep);
|
|
|
|
|
clearLifeFromSublife(elseLifep);
|
|
|
|
|
ifLifep->lifeToAbove();
|
|
|
|
|
elseLifep->lifeToAbove();
|
|
|
|
|
delete ifLifep;
|
|
|
|
|
delete elseLifep;
|
|
|
|
|
}
|
|
|
|
|
@ -240,24 +345,25 @@ private:
|
|
|
|
|
// assignment in the body that's used in the cond. (And otherwise
|
|
|
|
|
// would because it only appears used after-the-fact. So, we model
|
|
|
|
|
// it as a IF statement, and just don't allow elimination of
|
|
|
|
|
// variables in the body.
|
|
|
|
|
LifeMap* prevLifeMapp = m_lifep;
|
|
|
|
|
LifeMap* condLifep = new LifeMap;
|
|
|
|
|
LifeMap* bodyLifep = new LifeMap;
|
|
|
|
|
// variables across the body.
|
|
|
|
|
LifeBlock* prevLifep = m_lifep;
|
|
|
|
|
LifeBlock* condLifep = new LifeBlock (prevLifep, m_statep);
|
|
|
|
|
LifeBlock* bodyLifep = new LifeBlock (prevLifep, m_statep);
|
|
|
|
|
{
|
|
|
|
|
m_lifep = condLifep;
|
|
|
|
|
nodep->precondsp()->iterateAndNext(*this);
|
|
|
|
|
nodep->condp()->iterateAndNext(*this);
|
|
|
|
|
m_lifep = prevLifep;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
m_lifep = bodyLifep;
|
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
m_lifep = prevLifep;
|
|
|
|
|
}
|
|
|
|
|
UINFO(4," joinfor"<<endl);
|
|
|
|
|
m_lifep = prevLifeMapp;
|
|
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
clearLifeFromSublife(condLifep);
|
|
|
|
|
clearLifeFromSublife(bodyLifep);
|
|
|
|
|
condLifep->lifeToAbove();
|
|
|
|
|
bodyLifep->lifeToAbove();
|
|
|
|
|
delete condLifep;
|
|
|
|
|
delete bodyLifep;
|
|
|
|
|
}
|
|
|
|
|
@ -267,6 +373,7 @@ private:
|
|
|
|
|
// Enter the function and trace it
|
|
|
|
|
nodep->funcp()->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstVar*, AstNUser*) {} // Don't want varrefs under it
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
@ -274,15 +381,47 @@ private:
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
LifeVisitor(AstNetlist* nodep) {
|
|
|
|
|
m_ifCount = 1;
|
|
|
|
|
m_lifep = new LifeMap;
|
|
|
|
|
LifeVisitor(AstCFunc* nodep, LifeState* statep) {
|
|
|
|
|
UINFO(4," LifeVisitor on "<<nodep<<endl);
|
|
|
|
|
m_statep = statep;
|
|
|
|
|
{
|
|
|
|
|
m_lifep = new LifeBlock (NULL, m_statep);
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
delete m_lifep; m_lifep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual ~LifeVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class LifeTopVisitor : public AstNVisitor {
|
|
|
|
|
// Visit all top nodes searching for functions that are entry points we want to start
|
|
|
|
|
// finding code within.
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
|
|
|
|
LifeState* m_statep; // Current state
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
|
|
|
|
if (nodep->entryPoint()) {
|
|
|
|
|
// Usage model 1: Simulate all C code, doing lifetime analysis
|
|
|
|
|
LifeVisitor visitor (nodep, m_statep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstVar*, AstNUser*) {} // Accelerate
|
|
|
|
|
virtual void visit(AstNodeStmt*, AstNUser*) {} // Accelerate
|
|
|
|
|
virtual void visit(AstNodeMath*, AstNUser*) {} // Accelerate
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
LifeTopVisitor(AstNetlist* nodep, LifeState* statep) {
|
|
|
|
|
m_statep = statep;
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
delete m_lifep;
|
|
|
|
|
}
|
|
|
|
|
virtual ~LifeVisitor() {
|
|
|
|
|
V3Stats::addStat("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
|
|
|
|
}
|
|
|
|
|
virtual ~LifeTopVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
@ -290,6 +429,6 @@ public:
|
|
|
|
|
|
|
|
|
|
void V3Life::lifeAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
// Delete lifelicate assignments
|
|
|
|
|
LifeVisitor visitor (nodep);
|
|
|
|
|
LifeState state;
|
|
|
|
|
LifeTopVisitor visitor (nodep, &state);
|
|
|
|
|
}
|
|
|
|
|
|