verilator/src/V3LinkJump.cpp
2012-03-20 16:01:53 -04:00

269 lines
9.5 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//*************************************************************************
// 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-2012 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
AstNodeModule* 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;
}
// Skip over variables as we'll just move them in a momement
// Also this would otherwise prevent us from using a label twice
// see t_func_return test.
while (underp && underp->castVar()) underp = underp->nextp();
if (underp) UINFO(5," Underpoint is "<<underp<<endl);
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(AstNodeModule* 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,
AstBitPacked(), 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)));
V3Number zero (nodep->fileline(), 32, 0); zero.isSigned(true);
AstNode* zerosp = new AstConst(nodep->fileline(), zero);
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);
}