mirror of
https://github.com/verilator/verilator.git
synced 2025-02-02 03:34:05 +00:00
263 lines
9.2 KiB
C++
263 lines
9.2 KiB
C++
//*************************************************************************
|
||
// DESCRIPTION: Verilator: Replace return/continue with jumps
|
||
//
|
||
// Code available from: http://www.veripool.org/verilator
|
||
//
|
||
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||
//
|
||
//*************************************************************************
|
||
//
|
||
// Copyright 2003-2011 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.
|
||
//
|
||
//*************************************************************************
|
||
// V3LinkJump's Transformations:
|
||
//
|
||
// Each module:
|
||
// Look for BEGINs
|
||
// BEGIN(VAR...) -> VAR ... {renamed}
|
||
// FOR -> WHILEs
|
||
//
|
||
//*************************************************************************
|
||
|
||
#include "config_build.h"
|
||
#include "verilatedos.h"
|
||
#include <cstdio>
|
||
#include <cstdarg>
|
||
#include <unistd.h>
|
||
#include <algorithm>
|
||
#include <vector>
|
||
|
||
#include "V3Global.h"
|
||
#include "V3LinkJump.h"
|
||
#include "V3Ast.h"
|
||
|
||
//######################################################################
|
||
|
||
class LinkJumpVisitor : public AstNVisitor {
|
||
private:
|
||
// TYPES
|
||
typedef vector<AstBegin*> BeginStack;
|
||
|
||
// STATE
|
||
AstModule* m_modp; // Current module
|
||
AstNodeFTask* m_ftaskp; // Current function/task
|
||
AstWhile* m_loopp; // Current loop
|
||
bool m_loopInc; // In loop increment
|
||
int m_repeatNum; // Repeat counter
|
||
BeginStack m_beginStack; // All begin blocks above current node
|
||
|
||
// METHODS
|
||
static int debug() {
|
||
static int level = -1;
|
||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||
return level;
|
||
}
|
||
|
||
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
|
||
// Put label under given node, and if WHILE optionally at end of iteration
|
||
UINFO(4,"Create label for "<<nodep<<endl);
|
||
if (nodep->castJumpLabel()) return nodep->castJumpLabel(); // Done
|
||
|
||
AstNode* underp = NULL;
|
||
bool under_and_next = true;
|
||
if (nodep->castBegin()) underp = nodep->castBegin()->stmtsp();
|
||
else if (nodep->castNodeFTask()) underp = nodep->castNodeFTask()->stmtsp();
|
||
else if (nodep->castWhile()) {
|
||
if (endOfIter) {
|
||
// Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip
|
||
underp = nodep->castWhile()->bodysp();
|
||
} else {
|
||
underp = nodep; under_and_next=false; // IE we skip the entire while
|
||
}
|
||
}
|
||
else {
|
||
nodep->v3fatalSrc("Unknown jump point for break/disable/continue");
|
||
return NULL;
|
||
}
|
||
|
||
if (!underp) {
|
||
nodep->v3fatalSrc("Break/disable/continue not under expected statement");
|
||
return NULL;
|
||
} else if (underp->castJumpLabel()) {
|
||
return underp->castJumpLabel();
|
||
} else { // Move underp stuff to be under a new label
|
||
AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL);
|
||
|
||
AstNRelinker repHandle;
|
||
if (under_and_next) underp->unlinkFrBackWithNext(&repHandle);
|
||
else underp->unlinkFrBack(&repHandle);
|
||
repHandle.relink(labelp);
|
||
|
||
labelp->addStmtsp(underp);
|
||
// Keep any AstVars under the function not under the new JumpLabel
|
||
for (AstNode* nextp, *varp=underp; varp; varp = nextp) {
|
||
nextp = varp->nextp();
|
||
if (varp->castVar()) {
|
||
labelp->addPrev(varp->unlinkFrBack());
|
||
}
|
||
}
|
||
return labelp;
|
||
}
|
||
}
|
||
|
||
// VISITORS
|
||
virtual void visit(AstModule* nodep, AstNUser*) {
|
||
m_modp = nodep;
|
||
m_repeatNum = 0;
|
||
nodep->iterateChildren(*this);
|
||
m_modp = NULL;
|
||
}
|
||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||
m_ftaskp = nodep;
|
||
nodep->iterateChildren(*this);
|
||
m_ftaskp = NULL;
|
||
}
|
||
virtual void visit(AstBegin* nodep, AstNUser*) {
|
||
UINFO(8," "<<nodep<<endl);
|
||
m_beginStack.push_back(nodep);
|
||
nodep->iterateChildren(*this);
|
||
m_beginStack.pop_back();
|
||
}
|
||
virtual void visit(AstRepeat* nodep, AstNUser*) {
|
||
// So later optimizations don't need to deal with them,
|
||
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
|
||
// Note var can be signed or unsigned based on original number.
|
||
AstNode* countp = nodep->countp()->unlinkFrBackWithNext();
|
||
string name = string("__Vrepeat")+cvtToStr(m_repeatNum++);
|
||
// Spec says value is integral, if negative is ignored
|
||
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name,
|
||
AstLogicPacked(), 32);
|
||
varp->numeric(AstNumeric::SIGNED);
|
||
varp->dtypep()->numeric(AstNumeric::SIGNED);
|
||
varp->usedLoopIdx(true);
|
||
m_modp->addStmtp(varp);
|
||
AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
|
||
countp);
|
||
AstNode* decp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true),
|
||
new AstSub(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
|
||
new AstConst(nodep->fileline(), 1)));
|
||
AstNode* zerosp = new AstConst(nodep->fileline(), 0); zerosp->numeric(AstNumeric::SIGNED);
|
||
AstNode* condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false),
|
||
zerosp);
|
||
AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext();
|
||
AstNode* newp = new AstWhile(nodep->fileline(),
|
||
condp,
|
||
bodysp,
|
||
decp);
|
||
initsp = initsp->addNext(newp);
|
||
newp = initsp;
|
||
nodep->replaceWith(newp);
|
||
nodep->deleteTree(); nodep=NULL;
|
||
}
|
||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||
// Don't need to track AstRepeat/AstFor as they have already been converted
|
||
AstWhile* lastLoopp = m_loopp;
|
||
bool lastInc = m_loopInc;
|
||
m_loopp = nodep;
|
||
m_loopInc = false;
|
||
nodep->precondsp()->iterateAndNext(*this);
|
||
nodep->condp()->iterateAndNext(*this);
|
||
nodep->bodysp()->iterateAndNext(*this);
|
||
m_loopInc = true;
|
||
nodep->incsp()->iterateAndNext(*this);
|
||
m_loopInc = lastInc;
|
||
m_loopp = lastLoopp;
|
||
}
|
||
virtual void visit(AstReturn* nodep, AstNUser*) {
|
||
nodep->iterateChildren(*this);
|
||
AstFunc* funcp = m_ftaskp->castFunc();
|
||
if (!m_ftaskp) { nodep->v3error("Return isn't underneath a task or function"); }
|
||
else if (funcp && !nodep->lhsp()) { nodep->v3error("Return underneath a function should have return value"); }
|
||
else if (!funcp && nodep->lhsp()) { nodep->v3error("Return underneath a task shouldn't have return value"); }
|
||
else {
|
||
if (funcp && nodep->lhsp()) {
|
||
// Set output variable to return value
|
||
nodep->addPrev(new AstAssign(nodep->fileline(),
|
||
new AstVarRef(nodep->fileline(), funcp->fvarp()->castVar(), true),
|
||
nodep->lhsp()->unlinkFrBackWithNext()));
|
||
}
|
||
// Jump to the end of the function call
|
||
AstJumpLabel* labelp = findAddLabel(m_ftaskp, false);
|
||
nodep->addPrev(new AstJumpGo(nodep->fileline(), labelp));
|
||
}
|
||
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
||
}
|
||
virtual void visit(AstBreak* nodep, AstNUser*) {
|
||
nodep->iterateChildren(*this);
|
||
if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); }
|
||
else {
|
||
// Jump to the end of the loop
|
||
AstJumpLabel* labelp = findAddLabel(m_loopp, false);
|
||
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
|
||
}
|
||
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
||
}
|
||
virtual void visit(AstContinue* nodep, AstNUser*) {
|
||
nodep->iterateChildren(*this);
|
||
if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); }
|
||
else {
|
||
// Jump to the end of this iteration
|
||
// If a "for" loop then need to still do the post-loop increment
|
||
AstJumpLabel* labelp = findAddLabel(m_loopp, true);
|
||
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
|
||
}
|
||
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
||
}
|
||
virtual void visit(AstDisable* nodep, AstNUser*) {
|
||
UINFO(8," DISABLE "<<nodep<<endl);
|
||
nodep->iterateChildren(*this);
|
||
AstBegin* beginp = NULL;
|
||
for (BeginStack::reverse_iterator it = m_beginStack.rbegin(); it != m_beginStack.rend(); ++it) {
|
||
UINFO(9," UNDERBLK "<<*it<<endl);
|
||
if ((*it)->name() == nodep->name()) {
|
||
beginp = *it;
|
||
break;
|
||
}
|
||
}
|
||
//if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labeli: "); }
|
||
if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: "<<nodep->name()); }
|
||
else {
|
||
// Jump to the end of the named begin
|
||
AstJumpLabel* labelp = findAddLabel(beginp, false);
|
||
nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp));
|
||
}
|
||
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
||
//if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labelo: "); }
|
||
}
|
||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||
if (m_loopInc && nodep->varp()) nodep->varp()->usedLoopIdx(true);
|
||
}
|
||
|
||
virtual void visit(AstConst* nodep, AstNUser*) {}
|
||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||
nodep->iterateChildren(*this);
|
||
}
|
||
public:
|
||
// CONSTUCTORS
|
||
LinkJumpVisitor(AstNetlist* nodep) {
|
||
m_modp = NULL;
|
||
m_ftaskp = NULL;
|
||
m_loopp = NULL;
|
||
m_loopInc = false;
|
||
m_repeatNum = 0;
|
||
nodep->accept(*this);
|
||
}
|
||
virtual ~LinkJumpVisitor() {}
|
||
};
|
||
|
||
//######################################################################
|
||
// Task class functions
|
||
|
||
void V3LinkJump::linkJump(AstNetlist* nodep) {
|
||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||
LinkJumpVisitor bvisitor (nodep);
|
||
}
|