Use AstNode::foreach in V3Gate
This yields a little speedup.
This commit is contained in:
parent
152a6cd886
commit
2ab6272cc7
|
|
@ -2040,7 +2040,12 @@ template <> inline bool AstNode::mayBeUnder<AstNodeAssign>(const AstNode* nodep)
|
||||||
return !VN_IS(nodep, NodeMath);
|
return !VN_IS(nodep, NodeMath);
|
||||||
}
|
}
|
||||||
template <> inline bool AstNode::mayBeUnder<AstVarScope>(const AstNode* nodep) {
|
template <> inline bool AstNode::mayBeUnder<AstVarScope>(const AstNode* nodep) {
|
||||||
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
|
if (VN_IS(nodep, VarScope)) return false; // Should not nest
|
||||||
|
if (VN_IS(nodep, Var)) return false;
|
||||||
|
if (VN_IS(nodep, Active)) return false;
|
||||||
|
if (VN_IS(nodep, NodeStmt)) return false;
|
||||||
|
if (VN_IS(nodep, NodeMath)) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
template <> inline bool AstNode::mayBeUnder<AstExecGraph>(const AstNode* nodep) {
|
template <> inline bool AstNode::mayBeUnder<AstExecGraph>(const AstNode* nodep) {
|
||||||
if (VN_IS(nodep, ExecGraph)) return false; // Should not nest
|
if (VN_IS(nodep, ExecGraph)) return false; // Should not nest
|
||||||
|
|
|
||||||
181
src/V3Gate.cpp
181
src/V3Gate.cpp
|
|
@ -34,9 +34,10 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
class GateDedupeVarVisitor;
|
||||||
|
|
||||||
using GateVarRefList = std::list<AstNodeVarRef*>;
|
using GateVarRefList = std::list<AstNodeVarRef*>;
|
||||||
|
|
||||||
constexpr int GATE_DEDUP_MAX_DEPTH = 20;
|
constexpr int GATE_DEDUP_MAX_DEPTH = 20;
|
||||||
|
|
@ -297,6 +298,12 @@ public:
|
||||||
const GateVarRefList& rhsVarRefs() const { return m_rhsVarRefs; }
|
const GateVarRefList& rhsVarRefs() const { return m_rhsVarRefs; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Replace refs to 'varscp' with 'substp' in 'consumerp'
|
||||||
|
|
||||||
|
static bool eliminate(AstNode* consumerp, AstVarScope* varscp, AstNode* substp,
|
||||||
|
GateDedupeVarVisitor* varVisp);
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Gate class functions
|
// Gate class functions
|
||||||
|
|
||||||
|
|
@ -368,9 +375,23 @@ private:
|
||||||
return vertexp;
|
return vertexp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
|
||||||
|
if (debug() >= 5) consumerp->dumpTree(cout, " elimUsePre: ");
|
||||||
|
const bool replaced = eliminate(consumerp, varscp, substp, nullptr);
|
||||||
|
if (!replaced) return;
|
||||||
|
|
||||||
|
if (debug() >= 9) consumerp->dumpTree(cout, " elimUseCns: ");
|
||||||
|
// Caution: Can't let V3Const change our handle to consumerp, such as by
|
||||||
|
// optimizing away this assignment, etc.
|
||||||
|
consumerp = V3Const::constifyEdit(consumerp);
|
||||||
|
if (debug() >= 5) consumerp->dumpTree(cout, " elimUseDne: ");
|
||||||
|
// Some previous input edges may have disappeared, perhaps all of them.
|
||||||
|
// If we remove the edges we can further optimize
|
||||||
|
// See e.g t_var_overzero.v.
|
||||||
|
}
|
||||||
|
|
||||||
void optimizeSignals(bool allowMultiIn);
|
void optimizeSignals(bool allowMultiIn);
|
||||||
bool elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor);
|
bool elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor);
|
||||||
void optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp);
|
|
||||||
void warnSignals();
|
void warnSignals();
|
||||||
void consumedMark();
|
void consumedMark();
|
||||||
void consumedMarkRecurse(GateEitherVertex* vertexp);
|
void consumedMarkRecurse(GateEitherVertex* vertexp);
|
||||||
|
|
@ -817,83 +838,6 @@ void GateVisitor::warnSignals() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Push constant into expressions and reevaluate
|
|
||||||
|
|
||||||
class GateDedupeVarVisitor;
|
|
||||||
|
|
||||||
class GateElimVisitor final : public GateBaseVisitor {
|
|
||||||
private:
|
|
||||||
// NODE STATE
|
|
||||||
// STATE
|
|
||||||
const AstVarScope* const m_elimVarScp; // Variable being eliminated
|
|
||||||
AstNode* const m_replaceTreep; // What to replace the variable with
|
|
||||||
bool m_didReplace = false; // Did we do any replacements
|
|
||||||
GateDedupeVarVisitor* const m_varVisp; // Callback to keep hash up to date
|
|
||||||
|
|
||||||
// METHODS
|
|
||||||
void hashReplace(AstNode* oldp, AstNode* newp);
|
|
||||||
|
|
||||||
// VISITORS
|
|
||||||
virtual void visit(AstNodeVarRef* nodep) override {
|
|
||||||
if (nodep->varScopep() == m_elimVarScp) {
|
|
||||||
// Substitute in the new tree
|
|
||||||
// It's possible we substitute into something that will be reduced more later,
|
|
||||||
// however, as we never delete the top Always/initial statement, all should be well.
|
|
||||||
m_didReplace = true;
|
|
||||||
UASSERT_OBJ(nodep->access().isReadOnly(), nodep,
|
|
||||||
"Can't replace lvalue assignments with const var");
|
|
||||||
AstNode* const substp = m_replaceTreep->cloneTree(false);
|
|
||||||
UASSERT_OBJ(!(VN_IS(substp, NodeVarRef) && nodep->same(substp)),
|
|
||||||
// Prevent an infinite loop...
|
|
||||||
substp, "Replacing node with itself; perhaps circular logic?");
|
|
||||||
// Which fileline() to use?
|
|
||||||
// If replacing with logic, an error/warning is likely to want to point to the logic
|
|
||||||
// IE what we're replacing with.
|
|
||||||
// However a VARREF should point to the original as it's otherwise confusing
|
|
||||||
// to throw warnings that point to a PIN rather than where the pin us used.
|
|
||||||
if (VN_IS(substp, VarRef)) substp->fileline(nodep->fileline());
|
|
||||||
// Make the substp an rvalue like nodep. This facilitates the hashing in dedupe.
|
|
||||||
if (AstNodeVarRef* const varrefp = VN_CAST(substp, NodeVarRef))
|
|
||||||
varrefp->access(VAccess::READ);
|
|
||||||
hashReplace(nodep, substp);
|
|
||||||
nodep->replaceWith(substp);
|
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
// CONSTRUCTORS
|
|
||||||
virtual ~GateElimVisitor() override = default;
|
|
||||||
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep,
|
|
||||||
GateDedupeVarVisitor* varVisp)
|
|
||||||
: m_elimVarScp{varscp}
|
|
||||||
, m_replaceTreep{replaceTreep}
|
|
||||||
, m_varVisp{varVisp} {
|
|
||||||
UINFO(9, " elimvisitor " << nodep << endl);
|
|
||||||
UINFO(9, " elim varscp " << varscp << endl);
|
|
||||||
UINFO(9, " elim repce " << replaceTreep << endl);
|
|
||||||
iterate(nodep);
|
|
||||||
}
|
|
||||||
bool didReplace() const { return m_didReplace; }
|
|
||||||
};
|
|
||||||
|
|
||||||
void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
|
|
||||||
if (debug() >= 5) consumerp->dumpTree(cout, " elimUsePre: ");
|
|
||||||
const GateElimVisitor elimVisitor{consumerp, varscp, substp, nullptr};
|
|
||||||
if (elimVisitor.didReplace()) {
|
|
||||||
if (debug() >= 9) consumerp->dumpTree(cout, " elimUseCns: ");
|
|
||||||
// Caution: Can't let V3Const change our handle to consumerp, such as by
|
|
||||||
// optimizing away this assignment, etc.
|
|
||||||
consumerp = V3Const::constifyEdit(consumerp);
|
|
||||||
if (debug() >= 5) consumerp->dumpTree(cout, " elimUseDne: ");
|
|
||||||
// Some previous input edges may have disappeared, perhaps all of them.
|
|
||||||
// If we remove the edges we can further optimize
|
|
||||||
// See e.g t_var_overzero.v.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Auxiliary hash class for GateDedupeVarVisitor
|
// Auxiliary hash class for GateDedupeVarVisitor
|
||||||
|
|
||||||
|
|
@ -1113,9 +1057,47 @@ public:
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
||||||
void GateElimVisitor::hashReplace(AstNode* oldp, AstNode* newp) {
|
static bool eliminate(AstNode* consumerp, AstVarScope* varscp, AstNode* substp,
|
||||||
UINFO(9, "hashReplace " << (void*)oldp << " -> " << (void*)newp << endl);
|
GateDedupeVarVisitor* varVisp) {
|
||||||
if (m_varVisp) m_varVisp->hashReplace(oldp, newp);
|
UINFO(9, " Eliminating inside: " << consumerp << endl);
|
||||||
|
UINFO(9, " Eliminating varscopep: " << varscp << endl);
|
||||||
|
UINFO(9, " Eliminating substitute: " << substp << endl);
|
||||||
|
|
||||||
|
bool didReplace = false; // Did we do any replacements
|
||||||
|
consumerp->foreach<AstNodeVarRef>([=, &didReplace](AstNodeVarRef* nodep) {
|
||||||
|
if (nodep->varScopep() != varscp) return;
|
||||||
|
|
||||||
|
// Substitute in the new tree
|
||||||
|
#ifdef VL_DEBUG // Can be hot code, so expensive
|
||||||
|
UASSERT_OBJ(nodep->access().isReadOnly(), nodep,
|
||||||
|
"Can't replace lvalue assignments with const var");
|
||||||
|
UASSERT_OBJ(!(VN_IS(substp, NodeVarRef) && nodep->same(substp)),
|
||||||
|
// Prevent an infinite loop...
|
||||||
|
substp, "Replacing node with itself; perhaps circular logic?");
|
||||||
|
#endif
|
||||||
|
// It's possible we substitute into something that will be reduced more later,
|
||||||
|
// however, as we never delete the top Always/initial statement, all should be well.
|
||||||
|
didReplace = true;
|
||||||
|
|
||||||
|
// The replacement
|
||||||
|
AstNode* const newp = substp->cloneTree(false);
|
||||||
|
// Which fileline() to use? If replacing with logic, an error/warning is likely to want
|
||||||
|
// to point to the logic IE what we're replacing with. However, a VARREF should point
|
||||||
|
// to the original as it's otherwise confusing to throw warnings that point to a PIN
|
||||||
|
// rather than where the pin us used.
|
||||||
|
if (VN_IS(newp, VarRef)) newp->fileline(nodep->fileline());
|
||||||
|
// Make the newp an rvalue like nodep. This facilitates the hashing in dedupe.
|
||||||
|
if (AstNodeVarRef* const varrefp = VN_CAST(newp, NodeVarRef)) {
|
||||||
|
varrefp->access(VAccess::READ);
|
||||||
|
}
|
||||||
|
// Update hash?
|
||||||
|
if (varVisp) varVisp->hashReplace(nodep, newp);
|
||||||
|
// Replace the node
|
||||||
|
nodep->replaceWith(newp);
|
||||||
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
|
});
|
||||||
|
|
||||||
|
return didReplace;
|
||||||
}
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
|
|
@ -1170,8 +1152,7 @@ private:
|
||||||
if (lvertexp == consumeVertexp) {
|
if (lvertexp == consumeVertexp) {
|
||||||
UINFO(9, "skipping as self-recirculates\n");
|
UINFO(9, "skipping as self-recirculates\n");
|
||||||
} else {
|
} else {
|
||||||
const GateElimVisitor elimVisitor(consumerp, vvertexp->varScp(),
|
eliminate(consumerp, vvertexp->varScp(), dupVarRefp, &m_varVisitor);
|
||||||
dupVarRefp, &m_varVisitor);
|
|
||||||
}
|
}
|
||||||
outedgep = outedgep->relinkFromp(dupVvertexp);
|
outedgep = outedgep->relinkFromp(dupVvertexp);
|
||||||
}
|
}
|
||||||
|
|
@ -1571,12 +1552,13 @@ void GateVisitor::decomposeClkVectors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Convert VARSCOPE(ASSIGN(default, VARREF)) to just VARSCOPE(default)
|
// Gate class functions
|
||||||
|
|
||||||
class GateDeassignVisitor final : public GateBaseVisitor {
|
void V3Gate::gateAll(AstNetlist* nodep) {
|
||||||
private:
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||||
// VISITORS
|
{ const GateVisitor visitor{nodep}; } // Destruct before checking
|
||||||
virtual void visit(AstVarScope* nodep) override {
|
|
||||||
|
nodep->foreach<AstVarScope>([](AstVarScope* nodep) {
|
||||||
if (AstNodeAssign* const assp = VN_CAST(nodep->valuep(), NodeAssign)) {
|
if (AstNodeAssign* const assp = VN_CAST(nodep->valuep(), NodeAssign)) {
|
||||||
UINFO(5, " Removeassign " << assp << endl);
|
UINFO(5, " Removeassign " << assp << endl);
|
||||||
AstNode* const valuep = assp->rhsp();
|
AstNode* const valuep = assp->rhsp();
|
||||||
|
|
@ -1584,26 +1566,7 @@ private:
|
||||||
assp->replaceWith(valuep);
|
assp->replaceWith(valuep);
|
||||||
VL_DO_DANGLING(assp->deleteTree(), assp);
|
VL_DO_DANGLING(assp->deleteTree(), assp);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
// Speedups
|
|
||||||
virtual void visit(AstVar*) override {} // Accelerate
|
|
||||||
virtual void visit(AstActive*) override {} // Accelerate
|
|
||||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
// CONSTRUCTORS
|
|
||||||
explicit GateDeassignVisitor(AstNode* nodep) { iterate(nodep); }
|
|
||||||
virtual ~GateDeassignVisitor() override = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
//######################################################################
|
|
||||||
// Gate class functions
|
|
||||||
|
|
||||||
void V3Gate::gateAll(AstNetlist* nodep) {
|
|
||||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
||||||
{
|
|
||||||
const GateVisitor visitor{nodep};
|
|
||||||
GateDeassignVisitor{nodep};
|
|
||||||
} // Destruct before checking
|
|
||||||
V3Global::dumpCheckGlobalTree("gate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
V3Global::dumpCheckGlobalTree("gate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue