2018-06-23 21:07:22 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Recreate loops to help pack caches
|
|
|
|
|
//
|
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2003-2018 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Reloop's Transformations:
|
|
|
|
|
//
|
|
|
|
|
// Each CFunc:
|
|
|
|
|
// Look for a series of assignments that would look better in a loop:
|
|
|
|
|
//
|
|
|
|
|
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #))
|
|
|
|
|
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1))
|
|
|
|
|
// ->
|
|
|
|
|
// Create __Vilp local variable
|
|
|
|
|
// FOR(__Vilp = low; __Vilp <= high; ++__Vlip)
|
|
|
|
|
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp))
|
|
|
|
|
//
|
|
|
|
|
// Likewise vector assign to the same constant converted to a loop.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2018-10-14 11:04:18 +00:00
|
|
|
|
#include <algorithm>
|
2018-06-23 21:07:22 +00:00
|
|
|
|
#include <cstdarg>
|
2018-10-14 11:04:18 +00:00
|
|
|
|
#include <cstdio>
|
2018-06-23 21:07:22 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Reloop.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
2018-08-23 09:09:12 +00:00
|
|
|
|
#define RELOOP_MIN_ITERS 40 // Need at least this many loops to do this optimization
|
2018-06-23 21:07:22 +00:00
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class ReloopVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// TYPES
|
|
|
|
|
typedef std::vector<AstNodeAssign*> AssVec;
|
|
|
|
|
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstCFunc::user1p -> Var* for temp var, 0=not set yet
|
|
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
V3Double0 m_statReloops; // Statistic tracking
|
|
|
|
|
V3Double0 m_statReItems; // Statistic tracking
|
|
|
|
|
AstCFunc* m_cfuncp; // Current block
|
|
|
|
|
|
|
|
|
|
AssVec m_mgAssignps; // List of assignments merging
|
2018-06-24 15:22:56 +00:00
|
|
|
|
AstCFunc* m_mgCfuncp; // Parent C function
|
2018-06-23 21:07:22 +00:00
|
|
|
|
AstNode* m_mgNextp; // Next node
|
|
|
|
|
AstNodeSel* m_mgSelLp; // Parent select, NULL = idle
|
|
|
|
|
AstNodeSel* m_mgSelRp; // Parent select, NULL = constant
|
|
|
|
|
AstNodeVarRef* m_mgVarrefLp; // Parent varref
|
|
|
|
|
AstNodeVarRef* m_mgVarrefRp; // Parent varref, NULL = constant
|
|
|
|
|
AstConst* m_mgConstRp; // Parent RHS constant, NULL = sel
|
|
|
|
|
uint32_t m_mgIndexLo; // Merge range
|
|
|
|
|
uint32_t m_mgIndexHi; // Merge range
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
|
|
2018-06-24 15:22:56 +00:00
|
|
|
|
AstVar* findCreateVarTemp(FileLine* fl, AstCFunc* cfuncp) {
|
|
|
|
|
AstVar* varp = VN_CAST(cfuncp->user1p(), Var);
|
2018-06-23 21:07:22 +00:00
|
|
|
|
if (!varp) {
|
|
|
|
|
string newvarname = string("__Vilp");
|
|
|
|
|
varp = new AstVar(fl, AstVarType::STMTTEMP,
|
|
|
|
|
newvarname, VFlagLogicPacked(), 32);
|
2018-06-24 15:22:56 +00:00
|
|
|
|
if (!cfuncp) fl->v3fatalSrc("Assignment not under a function");
|
|
|
|
|
cfuncp->addInitsp(varp);
|
|
|
|
|
cfuncp->user1p(varp);
|
2018-06-23 21:07:22 +00:00
|
|
|
|
}
|
|
|
|
|
return varp;
|
|
|
|
|
}
|
|
|
|
|
void mergeEnd() {
|
|
|
|
|
if (!m_mgAssignps.empty()) {
|
|
|
|
|
uint32_t items = m_mgIndexHi - m_mgIndexLo + 1;
|
|
|
|
|
UINFO(9, "End merge iter="<<items<<" "<<m_mgIndexHi<<":"<<m_mgIndexLo
|
|
|
|
|
<<" "<<m_mgAssignps[0]<<endl);
|
|
|
|
|
if (items >= RELOOP_MIN_ITERS) {
|
|
|
|
|
UINFO(6, "Reloop merging items="<<items<<" "<<m_mgIndexHi<<":"<<m_mgIndexLo
|
|
|
|
|
<<" "<<m_mgAssignps[0]<<endl);
|
|
|
|
|
++m_statReloops;
|
|
|
|
|
m_statReItems += items;
|
|
|
|
|
|
|
|
|
|
// Transform first assign into for loop body
|
|
|
|
|
AstNodeAssign* bodyp = m_mgAssignps.front();
|
|
|
|
|
if (bodyp->lhsp() != m_mgSelLp) bodyp->v3fatalSrc("Corrupt queue/state");
|
|
|
|
|
FileLine* fl = bodyp->fileline();
|
|
|
|
|
AstVar* itp = findCreateVarTemp(fl, m_mgCfuncp);
|
|
|
|
|
|
|
|
|
|
AstNode* initp = new AstAssign(fl, new AstVarRef(fl, itp, true),
|
|
|
|
|
new AstConst(fl, m_mgIndexLo));
|
|
|
|
|
AstNode* condp = new AstLte(fl, new AstVarRef(fl, itp, false),
|
|
|
|
|
new AstConst(fl, m_mgIndexHi));
|
|
|
|
|
AstNode* incp = new AstAssign(fl, new AstVarRef(fl, itp, true),
|
|
|
|
|
new AstAdd(fl, new AstConst(fl, 1),
|
|
|
|
|
new AstVarRef(fl, itp, false)));
|
|
|
|
|
AstWhile* whilep = new AstWhile(fl, condp, NULL, incp);
|
|
|
|
|
initp->addNext(whilep);
|
|
|
|
|
bodyp->replaceWith(initp);
|
|
|
|
|
whilep->addBodysp(bodyp);
|
|
|
|
|
|
|
|
|
|
// Replace constant index with new loop index
|
|
|
|
|
AstNode* lbitp = m_mgSelLp->bitp();
|
|
|
|
|
lbitp->replaceWith(new AstVarRef(fl, itp, false));
|
|
|
|
|
lbitp->deleteTree(); VL_DANGLING(lbitp);
|
2018-08-23 09:09:12 +00:00
|
|
|
|
if (m_mgSelRp) { // else constant and no replace
|
2018-06-23 21:07:22 +00:00
|
|
|
|
AstNode* rbitp = m_mgSelRp->bitp();
|
|
|
|
|
rbitp->replaceWith(new AstVarRef(fl, itp, false));
|
|
|
|
|
rbitp->deleteTree(); VL_DANGLING(lbitp);
|
|
|
|
|
}
|
|
|
|
|
if (debug()>=9) initp->dumpTree(cout, "-new: ");
|
|
|
|
|
if (debug()>=9) whilep->dumpTree(cout, "-new: ");
|
|
|
|
|
|
|
|
|
|
// Remove remaining assigns
|
|
|
|
|
for (AssVec::iterator it=m_mgAssignps.begin(); it!=m_mgAssignps.end(); ++it) {
|
|
|
|
|
AstNodeAssign* assp = *it;
|
|
|
|
|
if (assp != bodyp) {
|
|
|
|
|
assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Setup for next merge
|
|
|
|
|
m_mgAssignps.clear();
|
|
|
|
|
m_mgSelLp = NULL;
|
|
|
|
|
m_mgSelRp = NULL;
|
|
|
|
|
m_mgVarrefLp = NULL;
|
|
|
|
|
m_mgVarrefRp = NULL;
|
|
|
|
|
m_mgConstRp = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstCFunc* nodep) {
|
|
|
|
|
m_cfuncp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
m_cfuncp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeAssign* nodep) {
|
|
|
|
|
if (!m_cfuncp) return;
|
|
|
|
|
|
|
|
|
|
// Left select WordSel or ArraySel
|
|
|
|
|
AstNodeSel* lselp = VN_CAST(nodep->lhsp(), NodeSel);
|
|
|
|
|
if (!lselp) { mergeEnd(); return; } // Not ever merged
|
|
|
|
|
// Of a constant index
|
|
|
|
|
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
|
|
|
|
if (!lbitp) { mergeEnd(); return; }
|
|
|
|
|
uint32_t index = lbitp->toUInt();
|
|
|
|
|
// Of variable
|
|
|
|
|
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
|
|
|
|
if (!lvarrefp) { mergeEnd(); return; }
|
|
|
|
|
|
|
|
|
|
// RHS is a constant or a select
|
|
|
|
|
AstConst* rconstp = VN_CAST(nodep->rhsp(), Const);
|
|
|
|
|
AstNodeSel* rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
|
|
|
|
AstNodeVarRef* rvarrefp = NULL;
|
|
|
|
|
if (rconstp) { // Ok
|
|
|
|
|
} else {
|
|
|
|
|
if (!rselp) { mergeEnd(); return; }
|
|
|
|
|
AstConst* rbitp = VN_CAST(rselp->bitp(), Const);
|
|
|
|
|
rvarrefp = VN_CAST(rselp->fromp(), NodeVarRef);
|
|
|
|
|
if (!rbitp || rbitp->toUInt() != index
|
|
|
|
|
|| !rvarrefp
|
|
|
|
|
|| lvarrefp->varp() == rvarrefp->varp()) {
|
|
|
|
|
mergeEnd(); return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 09:09:12 +00:00
|
|
|
|
if (m_mgSelLp) { // Old merge
|
2018-06-23 21:07:22 +00:00
|
|
|
|
if (m_mgCfuncp == m_cfuncp
|
|
|
|
|
&& m_mgNextp == nodep
|
|
|
|
|
&& m_mgSelLp->same(lselp)
|
|
|
|
|
&& m_mgVarrefLp->same(lvarrefp)
|
|
|
|
|
&& (m_mgConstRp
|
|
|
|
|
? (rconstp && m_mgConstRp->same(rconstp))
|
|
|
|
|
: (rselp
|
|
|
|
|
&& m_mgSelRp->same(rselp)
|
|
|
|
|
&& m_mgVarrefRp->same(rvarrefp)))
|
|
|
|
|
&& (index == m_mgIndexLo-1
|
|
|
|
|
|| index == m_mgIndexHi+1)) {
|
|
|
|
|
// Sequentially next to last assign; continue merge
|
|
|
|
|
if (index == m_mgIndexLo-1) m_mgIndexLo = index;
|
|
|
|
|
else if (index == m_mgIndexHi+1) m_mgIndexHi = index;
|
|
|
|
|
UINFO(9, "Continue merge i="<<index
|
|
|
|
|
<<" "<<m_mgIndexHi<<":"<<m_mgIndexLo<<" "<<nodep<<endl);
|
|
|
|
|
m_mgAssignps.push_back(nodep);
|
|
|
|
|
m_mgNextp = nodep->nextp();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// This assign doesn't merge with previous assign,
|
|
|
|
|
// but should start a new merge
|
|
|
|
|
mergeEnd();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Merge start
|
|
|
|
|
m_mgAssignps.push_back(nodep);
|
|
|
|
|
m_mgCfuncp = m_cfuncp;
|
|
|
|
|
m_mgNextp = nodep->nextp();
|
|
|
|
|
m_mgSelLp = lselp;
|
|
|
|
|
m_mgSelRp = rselp;
|
|
|
|
|
m_mgVarrefLp = lvarrefp;
|
|
|
|
|
m_mgVarrefRp = rvarrefp;
|
|
|
|
|
m_mgConstRp = rconstp;
|
|
|
|
|
m_mgIndexLo = index;
|
|
|
|
|
m_mgIndexHi = index;
|
|
|
|
|
UINFO(9, "Start merge i="<<index<<" "<<nodep<<endl);
|
|
|
|
|
}
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
virtual void visit(AstVar* nodep) {} // Speedup
|
|
|
|
|
virtual void visit(AstNodeMath* nodep) {} // Speedup
|
|
|
|
|
virtual void visit(AstNode* nodep) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
explicit ReloopVisitor(AstNetlist* nodep) {
|
|
|
|
|
m_cfuncp = NULL;
|
|
|
|
|
m_mgCfuncp = NULL;
|
|
|
|
|
m_mgNextp = NULL;
|
|
|
|
|
m_mgSelLp = NULL;
|
|
|
|
|
m_mgSelRp = NULL;
|
|
|
|
|
m_mgVarrefLp = NULL;
|
|
|
|
|
m_mgVarrefRp = NULL;
|
|
|
|
|
m_mgConstRp = NULL;
|
|
|
|
|
m_mgIndexLo = 0;
|
|
|
|
|
m_mgIndexHi = 0;
|
|
|
|
|
iterate(nodep);
|
|
|
|
|
}
|
|
|
|
|
virtual ~ReloopVisitor() {
|
|
|
|
|
V3Stats::addStat("Optimizations, Reloops", m_statReloops);
|
|
|
|
|
V3Stats::addStat("Optimizations, Reloop iterations", m_statReItems);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Reloop class functions
|
|
|
|
|
|
|
|
|
|
void V3Reloop::reloopAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
{
|
2018-08-23 09:09:12 +00:00
|
|
|
|
ReloopVisitor visitor(nodep);
|
2018-06-23 21:07:22 +00:00
|
|
|
|
} // Destruct before checking
|
|
|
|
|
V3Global::dumpCheckGlobalTree("reloop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
|
|
|
|
}
|