This patch implements #6480. All loop statements are represented using AstLoop and AstLoopTest. This necessitates rework of the loop unroller to handle loops of arbitrary form. To enable this, I have split the old unroller used for 'generate for' statements and moved it into V3Param, and subsequently rewrote V3Unroll to handle the new representation. V3Unroll can now unroll more complex loops, including with loop conditions containing multiple variable references or inlined functions. Handling the more generic code also requires some restrictions. If a loop contains any of the following, it cannot be unrolled: - A timing control that might suspend the loop - A non-inlined call to a non-pure function These constructs can change the values of variables in the loop, so are generally not safe to unroll if they are present. (We could still unroll if all the variables needed for unrolling are automatic, however we don't do that right now.) These restrictions seem ok in the benchmark suite, where the new unroller can generally unroll many more loops than before.
This commit is contained in:
parent
5c72b45975
commit
603f4c615a
|
|
@ -349,6 +349,7 @@ set(COMMON_SOURCES
|
|||
V3Undriven.cpp
|
||||
V3Unknown.cpp
|
||||
V3Unroll.cpp
|
||||
V3UnrollGen.cpp
|
||||
V3VariableOrder.cpp
|
||||
V3Waiver.cpp
|
||||
V3Width.cpp
|
||||
|
|
|
|||
|
|
@ -324,6 +324,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Undriven.o \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
V3UnrollGen.o \
|
||||
V3Width.o \
|
||||
V3WidthCommit.o \
|
||||
V3WidthSel.o \
|
||||
|
|
|
|||
|
|
@ -349,16 +349,21 @@ private:
|
|||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
cntVarp->funcLocal(true);
|
||||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||
beginp->addStmtsp(new AstWhile{
|
||||
nodep->fileline(),
|
||||
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}},
|
||||
controlp,
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}}});
|
||||
|
||||
{
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
flp, loopp,
|
||||
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}}});
|
||||
loopp->addStmtsp(controlp);
|
||||
loopp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}});
|
||||
beginp->addStmtsp(loopp);
|
||||
}
|
||||
nodep->replaceWith(beginp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -520,20 +520,6 @@ public:
|
|||
// * = Add a newline for $display
|
||||
bool addNewline() const { return displayType().addNewline(); }
|
||||
};
|
||||
class AstDoWhile final : public AstNodeStmt {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr)
|
||||
: ASTGEN_SUPER_DoWhile(fl) {
|
||||
condp(conditionp);
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDoWhile;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstDumpCtl final : public AstNodeStmt {
|
||||
// $dumpon etc
|
||||
// Parents: expr
|
||||
|
|
@ -677,6 +663,58 @@ public:
|
|||
}
|
||||
AstJumpBlock* blockp() const { return m_blockp; }
|
||||
};
|
||||
class AstLoop final : public AstNodeStmt {
|
||||
// An inifinite loop, used to model all source level procedural loops.
|
||||
// Executes as:
|
||||
// while (true) {
|
||||
// stmtsp;
|
||||
// // <- 'continue' inside 'stmtsp goes here
|
||||
// contsp;
|
||||
// }
|
||||
// 'contsp' is moved into 'stmtsp' in LinkJump when 'continue' statements are resovled.
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := contsp : List[AstNode] // Empty after LinkJump
|
||||
VOptionBool m_unroll; // Full, none, or default unrolling
|
||||
public:
|
||||
AstLoop(FileLine* fl)
|
||||
: ASTGEN_SUPER_Loop(fl) {}
|
||||
ASTGEN_MEMBERS_AstLoop;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool sameNode(const AstNode* thatp) const override {
|
||||
return m_unroll == VN_DBG_AS(thatp, Loop)->m_unroll;
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
// ACCESSORS
|
||||
VOptionBool unroll() const { return m_unroll; }
|
||||
void unroll(const VOptionBool flag) { m_unroll = flag; }
|
||||
};
|
||||
class AstLoopTest final : public AstNodeStmt {
|
||||
// The condition test inside an AstLoop. If the condition is true,
|
||||
// execution continues after this AstLoopTest statement. If the condition
|
||||
// is false, control is transfered to after the corresponding AstLoop.
|
||||
// In other words: AstLoopTest is like a conditional 'break' statement,
|
||||
// which breaks out of the loop if the condition is false.
|
||||
// @astgen op1 := condp : AstNodeExpr // The loop condition
|
||||
// @astgen ptr := m_loopp : AstLoop // The corresponding AstLoop
|
||||
public:
|
||||
AstLoopTest(FileLine* fl, AstLoop* loopp, AstNodeExpr* condp)
|
||||
: ASTGEN_SUPER_LoopTest(fl)
|
||||
, m_loopp{loopp} {
|
||||
this->condp(condp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstLoopTest;
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool sameNode(const AstNode*) const override { return true; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isBrancher() const override { return true; }
|
||||
int instrCount() const override { return 0; }
|
||||
// ACCESSORS
|
||||
AstLoop* loopp() const { return m_loopp; }
|
||||
};
|
||||
class AstMonitorOff final : public AstNodeStmt {
|
||||
const bool m_off; // Monitor off. Using 0=on allows faster init and comparison
|
||||
|
||||
|
|
@ -1044,28 +1082,6 @@ public:
|
|||
ASTGEN_MEMBERS_AstWaitFork;
|
||||
bool isTimingControl() const override { return true; }
|
||||
};
|
||||
class AstWhile final : public AstNodeStmt {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
// @astgen op3 := incsp : List[AstNode]
|
||||
VOptionBool m_unrollFull; // Full, disable, or default unrolling
|
||||
public:
|
||||
AstWhile(FileLine* fl, AstNodeExpr* condp, AstNode* stmtsp = nullptr, AstNode* incsp = nullptr)
|
||||
: ASTGEN_SUPER_While(fl) {
|
||||
this->condp(condp);
|
||||
addStmtsp(stmtsp);
|
||||
addIncsp(incsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstWhile;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
// Stop statement searchback here
|
||||
void addNextStmt(AstNode* newp, AstNode* belowp) override;
|
||||
VOptionBool unrollFull() const { return m_unrollFull; }
|
||||
void unrollFull(const VOptionBool flag) { m_unrollFull = flag; }
|
||||
};
|
||||
|
||||
// === AstNodeAssign ===
|
||||
class AstAssign final : public AstNodeAssign {
|
||||
|
|
|
|||
|
|
@ -1551,24 +1551,6 @@ void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
|
|||
this->addNextHere(newp);
|
||||
}
|
||||
|
||||
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||
// Special, as statements need to be put in different places
|
||||
// Belowp is how we came to recurse up to this point
|
||||
if (belowp == condp()) {
|
||||
// Becomes first statement in body, body may have been empty
|
||||
if (stmtsp()) {
|
||||
stmtsp()->addHereThisAsNext(newp);
|
||||
} else {
|
||||
addStmtsp(newp);
|
||||
}
|
||||
} else if (belowp == stmtsp()) {
|
||||
// Next statement in body
|
||||
belowp->addNextHere(newp);
|
||||
} else {
|
||||
belowp->v3fatalSrc("Doesn't look like this was really under the while");
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Per-type Debugging
|
||||
|
||||
|
|
@ -2065,6 +2047,30 @@ const char* AstJumpGo::broken() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void AstLoop::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (unroll().isSetTrue())
|
||||
str << " [unrollfull]";
|
||||
else if (unroll().isSetFalse())
|
||||
str << " [unrollnone]";
|
||||
}
|
||||
void AstLoop::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "unroll",
|
||||
unroll().isSetTrue() ? "full"
|
||||
: unroll().isSetFalse() ? "none"
|
||||
: "default");
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstLoopTest::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> ";
|
||||
loopp()->dump(str);
|
||||
}
|
||||
const char* AstLoopTest::broken() const {
|
||||
BROKEN_RTN(!loopp()->brokeExistsAbove());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AstMemberDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
|
||||
|
|
@ -2732,7 +2738,7 @@ void AstVar::dump(std::ostream& str) const {
|
|||
if (isSigUserRWPublic()) str << " [PWR]";
|
||||
if (isInternal()) str << " [INTERNAL]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOP]";
|
||||
if (isUsedLoopIdx()) str << " [LOOPIDX]";
|
||||
if (rand().isRandomizable()) str << rand();
|
||||
if (noReset()) str << " [!RST]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
|
|
@ -2783,13 +2789,6 @@ bool AstVar::sameNode(const AstNode* samep) const {
|
|||
const AstVar* const asamep = VN_DBG_AS(samep, Var);
|
||||
return name() == asamep->name() && varType() == asamep->varType();
|
||||
}
|
||||
void AstWhile::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (unrollFull().isSetTrue())
|
||||
str << " [unrollfull]";
|
||||
else if (unrollFull().isSetFalse())
|
||||
str << " [unrolldis]";
|
||||
}
|
||||
void AstScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [abovep=" << nodeAddr(aboveScopep()) << "]";
|
||||
|
|
|
|||
|
|
@ -415,11 +415,13 @@ static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar
|
|||
else
|
||||
incp = new AstSub{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
|
||||
|
||||
AstWhile* const whilep = new AstWhile{
|
||||
fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}};
|
||||
AstLoop* const loopp = new AstLoop{fl};
|
||||
loopp->addStmtsp(new AstLoopTest{fl, loopp, condp});
|
||||
loopp->addStmtsp(bodysp);
|
||||
loopp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp});
|
||||
AstNode* const stmtsp = varp; // New statements for outer loop
|
||||
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
|
||||
stmtsp->addNext(whilep);
|
||||
stmtsp->addNext(loopp);
|
||||
return stmtsp;
|
||||
}
|
||||
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
||||
|
|
@ -519,12 +521,14 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
|||
AstLogOr* const orp
|
||||
= new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
|
||||
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
|
||||
AstNode* const whilep = new AstWhile{fl, orp, first_clearp};
|
||||
AstLoop* const lp = new AstLoop{fl};
|
||||
lp->addStmtsp(new AstLoopTest{fl, lp, orp});
|
||||
lp->addStmtsp(first_clearp);
|
||||
first_clearp->addNext(bodyPointp);
|
||||
AstNode* const ifbodyp
|
||||
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}};
|
||||
ifbodyp->addNext(whilep);
|
||||
ifbodyp->addNext(lp);
|
||||
loopp = varp;
|
||||
loopp->addNext(first_varp);
|
||||
loopp->addNext(
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ std::string CfgBlock::name() const {
|
|||
ss << "if (";
|
||||
V3EmitV::debugVerilogForTree(ifp->condp(), ss);
|
||||
ss << ") ...";
|
||||
} else if (const AstWhile* const whilep = VN_CAST(nodep, While)) {
|
||||
ss << "while (";
|
||||
V3EmitV::debugVerilogForTree(whilep->condp(), ss);
|
||||
ss << ") ...";
|
||||
} else if (const AstLoopTest* const testp = VN_CAST(nodep, LoopTest)) {
|
||||
ss << "if (!";
|
||||
V3EmitV::debugVerilogForTree(testp->condp(), ss);
|
||||
ss << ") break;";
|
||||
} else {
|
||||
V3EmitV::debugVerilogForTree(nodep, ss);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ class CfgBuilder final : public VNVisitorConst {
|
|||
CfgBlock* m_currBBp = nullptr;
|
||||
// Continuation block for given JumpBlock
|
||||
std::unordered_map<AstJumpBlock*, CfgBlock*> m_jumpBlockContp;
|
||||
// Continuation block for given Loop
|
||||
std::unordered_map<AstLoop*, CfgBlock*> m_loopContp;
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -119,29 +121,40 @@ class CfgBuilder final : public VNVisitorConst {
|
|||
// Set continuation
|
||||
m_currBBp = contBBp;
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
if (!m_cfgp) return;
|
||||
|
||||
// Create the header block
|
||||
CfgBlock* const headBBp = m_cfgp->addBlock();
|
||||
m_cfgp->addTakenEdge(m_currBBp, headBBp);
|
||||
// Don't acutally need to add this 'nodep' to any block
|
||||
|
||||
// The While goes in the header block - semantically the condition check only ...
|
||||
m_currBBp = headBBp;
|
||||
addStmt(nodep);
|
||||
|
||||
// Create the body/continuation blocks
|
||||
CfgBlock* const bodyBBp = m_cfgp->addBlock();
|
||||
// Create continuation block
|
||||
CfgBlock* const contBBp = m_cfgp->addBlock();
|
||||
m_cfgp->addTakenEdge(headBBp, bodyBBp);
|
||||
m_cfgp->addUntknEdge(headBBp, contBBp);
|
||||
const bool newEntry = m_loopContp.emplace(nodep, contBBp).second;
|
||||
UASSERT_OBJ(newEntry, nodep, "AstLoop visited twice");
|
||||
|
||||
// Create the body block
|
||||
CfgBlock* const bodyBBp = m_cfgp->addBlock();
|
||||
m_cfgp->addTakenEdge(m_currBBp, bodyBBp);
|
||||
|
||||
// Build the body
|
||||
m_currBBp = bodyBBp;
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
iterateAndNextConstNull(nodep->incsp());
|
||||
if (!m_cfgp) return;
|
||||
if (m_currBBp) m_cfgp->addTakenEdge(m_currBBp, headBBp);
|
||||
if (m_currBBp) m_cfgp->addTakenEdge(m_currBBp, bodyBBp);
|
||||
|
||||
// Set continuation
|
||||
m_currBBp = contBBp;
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
if (!m_cfgp) return;
|
||||
|
||||
// Add terminator statement to current block - semantically the condition check only ...
|
||||
addStmt(nodep);
|
||||
|
||||
// Create continuation blocks
|
||||
CfgBlock* const contBBp = m_cfgp->addBlock();
|
||||
m_cfgp->addTakenEdge(m_currBBp, contBBp);
|
||||
m_cfgp->addUntknEdge(m_currBBp, m_loopContp.at(nodep->loopp()));
|
||||
|
||||
// Set continuation
|
||||
m_currBBp = contBBp;
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class CfgLiveVariables final : VNVisitorConst {
|
|||
|
||||
// Only the condition check belongs to the terminated basic block
|
||||
void visit(AstIf* nodep) override { single(nodep->condp()); }
|
||||
void visit(AstWhile* nodep) override { single(nodep->condp()); }
|
||||
void visit(AstLoopTest* nodep) override { single(nodep->condp()); }
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit CfgLiveVariables(const CfgGraph& cfg)
|
||||
|
|
|
|||
|
|
@ -268,7 +268,8 @@ class CleanVisitor final : public VNVisitor {
|
|||
ensureClean(nodep->condp());
|
||||
setClean(nodep, isClean(nodep->thenp()) && isClean(nodep->elsep()));
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
void visit(AstLoop* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->condp());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -926,7 +926,8 @@ class ConstVisitor final : public VNVisitor {
|
|||
bool m_doV = false; // Verilog, not C++ conversion
|
||||
bool m_doGenerate = false; // Postpone width checking inside generate
|
||||
bool m_convertLogicToBit = false; // Convert logical operators to bitwise
|
||||
bool m_hasJumpDelay = false; // JumpGo or Delay under this while
|
||||
bool m_hasJumpDelay = false; // JumpGo or Delay under this loop
|
||||
bool m_hasLoopTest = false; // Contains AstLoopTest
|
||||
bool m_underRecFunc = false; // Under a recursive function
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const AstArraySel* m_selp = nullptr; // Current select
|
||||
|
|
@ -3588,31 +3589,62 @@ class ConstVisitor final : public VNVisitor {
|
|||
// replaceWithSimulation on the Arg's parent FuncRef replaces these
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
void visit(AstLoop* nodep) override {
|
||||
VL_RESTORER(m_hasLoopTest);
|
||||
const bool oldHasJumpDelay = m_hasJumpDelay;
|
||||
m_hasJumpDelay = false;
|
||||
{ iterateChildren(nodep); }
|
||||
const bool thisWhileHasJumpDelay = m_hasJumpDelay;
|
||||
m_hasJumpDelay = thisWhileHasJumpDelay || oldHasJumpDelay;
|
||||
if (m_doNConst) {
|
||||
if (nodep->condp()->isZero()) {
|
||||
UINFO(4, "WHILE(0) => nop " << nodep);
|
||||
nodep->v3warn(UNUSEDLOOP,
|
||||
"Loop condition is always false; body will never execute");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else if (nodep->condp()->isNeqZero()) {
|
||||
if (!thisWhileHasJumpDelay) {
|
||||
nodep->v3warn(INFINITELOOP, "Infinite loop (condition always true)");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::INFINITELOOP,
|
||||
true); // Complain just once
|
||||
}
|
||||
} else if (operandBoolShift(nodep->condp())) {
|
||||
replaceBoolShift(nodep->condp());
|
||||
m_hasLoopTest = false;
|
||||
iterateChildren(nodep);
|
||||
bool thisLoopHasJumpDelay = m_hasJumpDelay;
|
||||
m_hasJumpDelay = thisLoopHasJumpDelay || oldHasJumpDelay;
|
||||
// If the first statement always break, the loop is useless
|
||||
if (AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
|
||||
if (testp->condp()->isZero()) {
|
||||
nodep->v3warn(UNUSEDLOOP, "Loop condition is always false");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If last statement always breaks, repalce loop with body
|
||||
if (AstNode* lastp = nodep->stmtsp()) {
|
||||
while (AstNode* const nextp = lastp->nextp()) lastp = nextp;
|
||||
if (AstLoopTest* const testp = VN_CAST(lastp, LoopTest)) {
|
||||
if (testp->condp()->isZero()) {
|
||||
VL_DO_DANGLING(pushDeletep(testp->unlinkFrBack()), testp);
|
||||
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Warn on infinite loop
|
||||
if (!m_hasLoopTest && !thisLoopHasJumpDelay) {
|
||||
nodep->v3warn(INFINITELOOP, "Infinite loop (condition always true)");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::INFINITELOOP, true); // Complain once
|
||||
}
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
// If never breaks, remove
|
||||
if (nodep->condp()->isNeqZero()) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
m_hasLoopTest = true;
|
||||
|
||||
// If always breaks, subsequent statements are dead code, delete them
|
||||
if (nodep->condp()->isZero()) {
|
||||
if (AstNode* const nextp = nodep->nextp()) {
|
||||
VL_DO_DANGLING(pushDeletep(nextp->unlinkFrBackWithNext()), nodep);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (operandBoolShift(nodep->condp())) replaceBoolShift(nodep->condp());
|
||||
}
|
||||
|
||||
void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
|
||||
|
|
@ -3674,6 +3706,11 @@ class ConstVisitor final : public VNVisitor {
|
|||
void visit(AstJumpBlock* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
// If first statement is an AstLoopTest, pull it before the jump block
|
||||
if (AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
|
||||
nodep->addHereThisAsNext(testp->unlinkFrBack());
|
||||
}
|
||||
|
||||
// Remove if empty
|
||||
if (!nodep->stmtsp()) {
|
||||
UINFO(4, "JUMPLABEL => empty " << nodep);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstIf::user1() -> bool. True indicates ifelse processed
|
||||
// AstIf::user2() -> bool. True indicates coverage-generated
|
||||
// AstIf/AstLoopTest::user2() -> bool. True indicates coverage-generated
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
|
||||
|
|
@ -153,7 +153,6 @@ class CoverageVisitor final : public VNVisitor {
|
|||
bool m_objective = false; // Expression objective
|
||||
bool m_ifCond = false; // Visiting if condition
|
||||
bool m_inToggleOff = false; // In function/task etc
|
||||
bool m_inLoopNotBody = false; // Inside a loop, but not in its body
|
||||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
|
||||
// STATE - cleared each module
|
||||
|
|
@ -285,23 +284,14 @@ class CoverageVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); }
|
||||
// we can cover expressions in while loops, but the counting goes outside
|
||||
// the while, see: "minimally-intelligent decision about ... clock domain"
|
||||
// in the Toggle Coverage docs
|
||||
void visit(AstWhile* nodep) override {
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
VL_RESTORER(m_state);
|
||||
VL_RESTORER(m_inToggleOff);
|
||||
m_inToggleOff = true;
|
||||
createHandle(nodep);
|
||||
{
|
||||
VL_RESTORER(m_inLoopNotBody);
|
||||
m_inLoopNotBody = true;
|
||||
iterateNull(nodep->condp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
}
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (m_state.lineCoverageOn(nodep)) {
|
||||
lineTrack(nodep);
|
||||
AstCoverOtherDecl* const declp
|
||||
= new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(),
|
||||
"block", linesCov(m_state, nodep), 0};
|
||||
|
|
@ -311,6 +301,21 @@ class CoverageVisitor final : public VNVisitor {
|
|||
insertProcStatement(nodep, newp);
|
||||
}
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
if (nodep->user2SetOnce()) return;
|
||||
lineTrack(nodep);
|
||||
if (m_state.lineCoverageOn(nodep) && nodep->backp()->nextp() == nodep) {
|
||||
AstCoverOtherDecl* const declp
|
||||
= new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(),
|
||||
"block", linesCov(m_state, nodep), 0};
|
||||
m_modp->addStmtsp(declp);
|
||||
AstNode* const newp
|
||||
= newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block"));
|
||||
nodep->addHereThisAsNext(newp);
|
||||
createHandle(nodep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
if (!nodep->dpiImport()) iterateProcedure(nodep);
|
||||
|
|
@ -321,7 +326,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
itemp->addStmtsp(stmtp);
|
||||
} else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) {
|
||||
itemp->addStmtsp(stmtp);
|
||||
} else if (AstWhile* const itemp = VN_CAST(nodep, While)) {
|
||||
} else if (AstLoop* const itemp = VN_CAST(nodep, Loop)) {
|
||||
itemp->addStmtsp(stmtp);
|
||||
} else if (AstIf* const itemp = VN_CAST(nodep, If)) {
|
||||
if (m_then) {
|
||||
|
|
@ -499,8 +504,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
return includeCondToBranchRecursive(backp);
|
||||
} else if (VN_IS(backp, Sel) && VN_AS(backp, Sel)->fromp() == nodep) {
|
||||
return includeCondToBranchRecursive(backp);
|
||||
} else if (VN_IS(backp, NodeAssign) && VN_AS(backp, NodeAssign)->rhsp() == nodep
|
||||
&& !m_inLoopNotBody) {
|
||||
} else if (VN_IS(backp, NodeAssign) && VN_AS(backp, NodeAssign)->rhsp() == nodep) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1298,7 +1298,7 @@ class DelayedVisitor final : public VNVisitor {
|
|||
// Record write reference
|
||||
recordWriteRef(nodep, false);
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
void visit(AstLoop* nodep) override {
|
||||
VL_RESTORER(m_inLoop);
|
||||
m_inLoop = true;
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ public:
|
|||
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
||||
VMemberMap m_memberMap;
|
||||
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
||||
std::unordered_map<AstJumpBlock*, size_t> m_labelNumbers; // Label numbers for JumpBlocks
|
||||
std::unordered_map<AstJumpBlock*, size_t> m_labelNumbers; // Label numbers for AstJumpBlocks
|
||||
bool m_inUC = false; // Inside an AstUCStmt or AstUCExpr
|
||||
bool m_emitConstInit = false; // Emitting constant initializer
|
||||
bool m_createdScopeHash = false; // Already created a scope hash
|
||||
|
|
@ -1095,21 +1095,47 @@ public:
|
|||
// Emit
|
||||
putns(nodep, "goto __Vlabel" + std::to_string(n) + ";\n");
|
||||
}
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
// Special case when the AstLoopTest is first for output readability
|
||||
if (AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
|
||||
putns(nodep, "while (");
|
||||
iterateConst(testp->condp());
|
||||
puts(") {\n");
|
||||
iterateAndNextConstNull(testp->nextp());
|
||||
puts("}\n");
|
||||
return;
|
||||
}
|
||||
// Special case when the AstLoopTest is last for output readability
|
||||
if (AstNode* lastp = nodep->stmtsp()) {
|
||||
while (AstNode* const nextp = lastp->nextp()) lastp = nextp;
|
||||
if (AstLoopTest* const testp = VN_CAST(lastp, LoopTest)) {
|
||||
putns(nodep, "do {\n");
|
||||
for (AstNode* p = nodep->stmtsp(); p != lastp; p = p->nextp()) iterateConst(p);
|
||||
puts("} while (");
|
||||
iterateConst(testp->condp());
|
||||
puts(");\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Emit generic case directly
|
||||
putns(nodep, "while (true) {\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
putns(nodep, "if (!(");
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
puts(")) break;\n");
|
||||
}
|
||||
void visit(AstCLocalScope* nodep) override {
|
||||
putns(nodep, "{\n");
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
VL_RESTORER(m_createdScopeHash);
|
||||
putns(nodep, "while (");
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
puts(") {\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
iterateAndNextConstNull(nodep->incsp());
|
||||
puts("}\n");
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
putns(nodep, "if (");
|
||||
if (!nodep->branchPred().unknown()) {
|
||||
|
|
|
|||
|
|
@ -399,14 +399,39 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
putfs(nodep, "end\n");
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
putfs(nodep, "while (");
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
puts(") begin\n");
|
||||
void visit(AstLoop* nodep) override {
|
||||
// Special case when the AstLoopTest is first for output readability
|
||||
if (AstLoopTest* const testp = VN_CAST(nodep->stmtsp(), LoopTest)) {
|
||||
putfs(nodep, "while (");
|
||||
iterateConst(testp->condp());
|
||||
puts(") begin\n");
|
||||
iterateAndNextConstNull(testp->nextp());
|
||||
puts("end\n");
|
||||
return;
|
||||
}
|
||||
// Special case when the AstLoopTest is last for output readability
|
||||
if (AstNode* lastp = nodep->stmtsp()) {
|
||||
while (AstNode* const nextp = lastp->nextp()) lastp = nextp;
|
||||
if (AstLoopTest* const testp = VN_CAST(lastp, LoopTest)) {
|
||||
putfs(nodep, "do begin\n");
|
||||
for (AstNode* p = nodep->stmtsp(); p != lastp; p = p->nextp()) iterateConst(p);
|
||||
puts("end while (");
|
||||
iterateConst(testp->condp());
|
||||
puts(")\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Generic case
|
||||
putfs(nodep, "while (true) begin\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
iterateAndNextConstNull(nodep->incsp());
|
||||
iterateAndNextConstNull(nodep->contsp());
|
||||
putfs(nodep, "end\n");
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
putfs(nodep, "if (!(");
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
puts(")) break;\n");
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
putfs(nodep, "");
|
||||
if (const AstIf* const ifp = VN_CAST(nodep, If)) {
|
||||
|
|
|
|||
|
|
@ -126,25 +126,26 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
|
|||
}
|
||||
puts("</if>\n");
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
outputTag(nodep, "while");
|
||||
void visit(AstLoop* nodep) override {
|
||||
outputTag(nodep, "loop");
|
||||
puts(">\n");
|
||||
if (nodep->condp()) {
|
||||
puts("<begin>\n");
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
puts("</begin>\n");
|
||||
}
|
||||
if (nodep->stmtsp()) {
|
||||
puts("<begin>\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
puts("</begin>\n");
|
||||
}
|
||||
if (nodep->incsp()) {
|
||||
if (nodep->contsp()) {
|
||||
puts("<begin>\n");
|
||||
iterateAndNextConstNull(nodep->incsp());
|
||||
iterateAndNextConstNull(nodep->contsp());
|
||||
puts("</begin>\n");
|
||||
}
|
||||
puts("</while>\n");
|
||||
puts("</loop>\n");
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
outputTag(nodep, "looptest");
|
||||
puts(">\n");
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
puts("</looptest>\n");
|
||||
}
|
||||
void visit(AstNetlist* nodep) override {
|
||||
puts("<netlist>\n");
|
||||
|
|
|
|||
|
|
@ -1229,9 +1229,9 @@ class GateUnused final {
|
|||
|
||||
if (const AstNodeProcedure* const procedurep = VN_CAST(nodep, NodeProcedure)) {
|
||||
if (procedurep->stmtsp())
|
||||
procedurep->stmtsp()->foreach([](const AstWhile* const whilep) { //
|
||||
whilep->v3warn(UNUSEDLOOP, "Loop is not used and will be optimized out");
|
||||
whilep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
procedurep->stmtsp()->foreach([](const AstLoop* const loopp) { //
|
||||
loopp->v3warn(UNUSEDLOOP, "Loop is not used and will be optimized out");
|
||||
loopp->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,37 +387,25 @@ class LifeVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(delete ifLifep, ifLifep);
|
||||
VL_DO_DANGLING(delete elseLifep, elseLifep);
|
||||
}
|
||||
|
||||
void visit(AstWhile* nodep) override {
|
||||
// While's are a problem, as we don't allow loops in the graph. We
|
||||
// may go around the cond/body multiple times. Thus a
|
||||
// lifelication just in the body is ok, but we can't delete an
|
||||
// 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 across the body.
|
||||
void visit(AstLoop* nodep) override {
|
||||
// Similar problem to AstJumpBlock, don't optimize loop bodies - most are unrolled
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
LifeBlock* const prevLifep = m_lifep;
|
||||
LifeBlock* const condLifep = new LifeBlock{prevLifep, m_statep};
|
||||
LifeBlock* const bodyLifep = new LifeBlock{prevLifep, m_statep};
|
||||
{
|
||||
m_lifep = condLifep;
|
||||
iterateAndNextNull(nodep->condp());
|
||||
}
|
||||
{
|
||||
VL_RESTORER(m_noopt);
|
||||
m_lifep = bodyLifep;
|
||||
setNoopt();
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
m_lifep = prevLifep;
|
||||
UINFO(4, " joinfor");
|
||||
UINFO(4, " joinloop");
|
||||
// For the next assignments, clear any variables that were read or written in the block
|
||||
condLifep->lifeToAbove();
|
||||
bodyLifep->lifeToAbove();
|
||||
VL_DO_DANGLING(delete condLifep, condLifep);
|
||||
VL_DO_DANGLING(delete bodyLifep, bodyLifep);
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
// As with While's we can't predict if a JumpGo will kill us or not
|
||||
// As with Loop's we can't predict if a JumpGo will kill us or not
|
||||
// It's worse though as an IF(..., JUMPGO) may change the control flow.
|
||||
// Just don't optimize blocks with labels; they're rare - so far.
|
||||
LifeBlock* const prevLifep = m_lifep;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ class LinkIncVisitor final : public VNVisitor {
|
|||
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
||||
AstNodeModule* m_modp = nullptr; // Module we're inside
|
||||
int m_modIncrementsNum = 0; // Var name counter
|
||||
AstWhile* m_inWhileCondp = nullptr; // Inside condition of this while loop
|
||||
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
||||
bool m_unsupportedHere = false; // Used to detect where it's not supported yet
|
||||
|
||||
|
|
@ -81,14 +80,12 @@ class LinkIncVisitor final : public VNVisitor {
|
|||
// Return node that must be visited, if any
|
||||
UINFOTREE(9, newp, "", "newstmt");
|
||||
UASSERT_OBJ(m_insStmtp, nodep, "Expression not underneath a statement");
|
||||
// In a while condition, the statement also needs to go on the
|
||||
// back-edge to the loop header, 'incsp' is that place.
|
||||
if (m_inWhileCondp) m_inWhileCondp->addIncsp(newp->cloneTreePure(true));
|
||||
m_insStmtp->addHereThisAsNext(newp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
if (nodep->dead()) return;
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_modIncrementsNum);
|
||||
m_modp = nodep;
|
||||
|
|
@ -113,20 +110,10 @@ class LinkIncVisitor final : public VNVisitor {
|
|||
}
|
||||
iterateAndNextNull(nodep->passsp());
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
// Special, as statements need to be put in different places
|
||||
m_insStmtp = nodep;
|
||||
{
|
||||
// Conditions insert before the loop and into incsp
|
||||
VL_RESTORER(m_inWhileCondp);
|
||||
m_inWhileCondp = nodep;
|
||||
iterateAndNextNull(nodep->condp());
|
||||
}
|
||||
// Body insert just before themselves
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
m_insStmtp = nullptr; // First thing should be new statement
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
// Done the loop
|
||||
m_insStmtp = nullptr; // Next thing should be new statement
|
||||
}
|
||||
void visit(AstNodeForeach* nodep) override {
|
||||
|
|
@ -181,6 +168,11 @@ class LinkIncVisitor final : public VNVisitor {
|
|||
iterateAndNextNull(nodep->stmtsp());
|
||||
m_insStmtp = nullptr;
|
||||
}
|
||||
void visit(AstBegin* nodep) override {
|
||||
m_insStmtp = nullptr; // Pretend not a statement TODO: parse ++/-- as ExprStmt
|
||||
iterateChildren(nodep);
|
||||
m_insStmtp = nullptr; // Next thing should be new statement
|
||||
}
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
m_insStmtp = nodep;
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -86,24 +86,18 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
} else if (AstForeach* const foreachp = VN_CAST(nodep, Foreach)) {
|
||||
if (endOfIter) {
|
||||
underp = foreachp->stmtsp();
|
||||
// Keep a LoopTest **at the front** outside the jump block
|
||||
if (VN_IS(underp, LoopTest)) underp = underp->nextp();
|
||||
} else {
|
||||
underp = nodep;
|
||||
under_and_next = false; // IE we skip the entire foreach
|
||||
}
|
||||
} else if (AstWhile* const whilep = VN_CAST(nodep, While)) {
|
||||
} else if (AstLoop* const loopp = VN_CAST(nodep, Loop)) {
|
||||
if (endOfIter) {
|
||||
underp = whilep->stmtsp();
|
||||
underp = loopp->stmtsp();
|
||||
} else {
|
||||
underp = nodep;
|
||||
under_and_next = false; // IE we skip the entire while
|
||||
}
|
||||
} else if (AstDoWhile* const dowhilep = VN_CAST(nodep, DoWhile)) {
|
||||
// Handle it the same as AstWhile, because it will be converted to it
|
||||
if (endOfIter) {
|
||||
underp = dowhilep->stmtsp();
|
||||
} else {
|
||||
underp = nodep;
|
||||
under_and_next = false;
|
||||
under_and_next = false; // IE we skip the entire loop
|
||||
}
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown jump point for break/disable/continue");
|
||||
|
|
@ -307,52 +301,34 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::READ}, zerosp};
|
||||
AstNode* const bodysp = nodep->stmtsp();
|
||||
if (bodysp) bodysp->unlinkFrBackWithNext();
|
||||
AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodysp, decp};
|
||||
if (!m_unrollFull.isDefault()) whilep->unrollFull(m_unrollFull);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, condp});
|
||||
loopp->addStmtsp(bodysp);
|
||||
loopp->addContsp(decp);
|
||||
if (!m_unrollFull.isDefault()) loopp->unroll(m_unrollFull);
|
||||
m_unrollFull = VOptionBool::OPT_DEFAULT_FALSE;
|
||||
beginp->addStmtsp(initsp);
|
||||
beginp->addStmtsp(whilep);
|
||||
beginp->addStmtsp(loopp);
|
||||
// Replacement AstBegin will be iterated next
|
||||
nodep->replaceWith(beginp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
// Don't need to track AstRepeat/AstFor as they have already been converted
|
||||
if (!m_unrollFull.isDefault()) nodep->unrollFull(m_unrollFull);
|
||||
if (m_modp->hasParameterList() || m_modp->hasGParam())
|
||||
void visit(AstLoop* nodep) override {
|
||||
if (!m_unrollFull.isDefault()) nodep->unroll(m_unrollFull);
|
||||
if (m_modp->hasParameterList() || m_modp->hasGParam()) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
}
|
||||
m_unrollFull = VOptionBool::OPT_DEFAULT_FALSE;
|
||||
VL_RESTORER(m_loopp);
|
||||
VL_RESTORER(m_loopInc);
|
||||
m_loopp = nodep;
|
||||
m_loopInc = false;
|
||||
iterateAndNextNull(nodep->condp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
m_loopInc = true;
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
}
|
||||
void visit(AstDoWhile* nodep) override {
|
||||
// It is converted to AstWhile in this visit method
|
||||
VL_RESTORER(m_loopp);
|
||||
{
|
||||
m_loopp = nodep;
|
||||
iterateAndNextNull(nodep->condp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
AstNodeExpr* const condp = nodep->condp() ? nodep->condp()->unlinkFrBack() : nullptr;
|
||||
AstNode* const bodyp = nodep->stmtsp() ? nodep->stmtsp()->unlinkFrBack() : nullptr;
|
||||
AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodyp};
|
||||
if (!m_unrollFull.isDefault()) whilep->unrollFull(m_unrollFull);
|
||||
m_unrollFull = VOptionBool::OPT_DEFAULT_FALSE;
|
||||
// No unused warning for converted AstDoWhile, as body always executes once
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
nodep->replaceWith(whilep);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
if (bodyp) {
|
||||
AstNode* const copiedBodyp = bodyp->cloneTree(false);
|
||||
addPrefixToBlocksRecurse("__Vdo_while1_", copiedBodyp);
|
||||
addPrefixToBlocksRecurse("__Vdo_while2_", bodyp);
|
||||
whilep->addHereThisAsNext(copiedBodyp);
|
||||
}
|
||||
iterateAndNextNull(nodep->contsp());
|
||||
// Move contsp into stmtsp, no longer needed to keep separately
|
||||
if (nodep->contsp()) nodep->addStmtsp(nodep->contsp()->unlinkFrBackWithNext());
|
||||
}
|
||||
void visit(AstNodeForeach* nodep) override {
|
||||
VL_RESTORER(m_loopp);
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
// Try very hard to avoid false positives
|
||||
AstNode* nextp = nodep->nextp();
|
||||
if (!childp) return;
|
||||
if (!nextp && VN_IS(nodep, While) && VN_IS(nodep->backp(), Begin))
|
||||
if (!nextp && VN_IS(nodep, Loop) && VN_IS(nodep->backp(), Begin))
|
||||
nextp = nodep->backp()->nextp();
|
||||
if (!nextp) return;
|
||||
if (VN_IS(childp, Begin) || VN_IS(childp, GenBlock)) return;
|
||||
|
|
@ -573,10 +573,15 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
checkIndent(nodep, nodep->stmtsp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstDoWhile* nodep) override {
|
||||
void visit(AstLoop* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
m_insideLoop = true;
|
||||
if (VN_IS(nodep->stmtsp(), LoopTest)) {
|
||||
checkIndent(nodep, nodep->stmtsp()->nextp());
|
||||
} else {
|
||||
checkIndent(nodep, nodep->stmtsp());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstWait* nodep) override {
|
||||
|
|
@ -590,13 +595,6 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
nodep->fileline(newfl);
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
m_insideLoop = true;
|
||||
checkIndent(nodep, nodep->stmtsp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
V3Control::applyModule(nodep);
|
||||
++m_statModules;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
#include "V3Hasher.h"
|
||||
#include "V3Os.h"
|
||||
#include "V3Parse.h"
|
||||
#include "V3Simulate.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Unroll.h"
|
||||
#include "V3Width.h"
|
||||
|
||||
|
|
@ -1267,7 +1269,7 @@ class ParamVisitor final : public VNVisitor {
|
|||
// STATE - across all visitors
|
||||
ParamState& m_state; // Common state
|
||||
ParamProcessor m_processor; // De-parameterize a cell, build modules
|
||||
UnrollStateful m_unroller; // Loop unroller
|
||||
GenForUnroller m_unroller; // Unroller for AstGenFor
|
||||
|
||||
bool m_iterateModule = false; // Iterating module body
|
||||
string m_unlinkedTxt; // Text for AstUnlinkedRef
|
||||
|
|
@ -1632,10 +1634,10 @@ class ParamVisitor final : public VNVisitor {
|
|||
// a BEGIN("zzz__BRA__{loop#}__KET__")
|
||||
const string beginName = nodep->name();
|
||||
// Leave the original Begin, as need a container for the (possible) GENVAR
|
||||
// Note V3Unroll will replace some AstVarRef's to the loop variable with constants
|
||||
// Note m_unroller will replace some AstVarRef's to the loop variable with constants
|
||||
// Don't remove any deleted nodes in m_unroller until whole process finishes,
|
||||
// (are held in m_unroller), as some AstXRefs may still point to old nodes.
|
||||
VL_DO_DANGLING(m_unroller.unrollGen(forp, beginName), forp);
|
||||
// as some AstXRefs may still point to old nodes.
|
||||
VL_DO_DANGLING(m_unroller.unroll(forp, beginName), forp);
|
||||
// Blocks were constructed under the special begin, move them up
|
||||
// Note forp is null, so grab statements again
|
||||
if (AstNode* const stmtsp = nodep->genforp()) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ class PremitVisitor final : public VNVisitor {
|
|||
AstCFunc* m_cfuncp = nullptr; // Current block
|
||||
size_t m_tmpVarCnt = 0; // Number of temporary variables created inside a function
|
||||
AstNode* m_stmtp = nullptr; // Current statement
|
||||
AstWhile* m_inWhileCondp = nullptr; // Inside condition of this while loop
|
||||
bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts
|
||||
|
||||
// METHODS
|
||||
|
|
@ -100,10 +99,6 @@ class PremitVisitor final : public VNVisitor {
|
|||
= new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, nodep};
|
||||
// Insert before the statement
|
||||
m_stmtp->addHereThisAsNext(assignp);
|
||||
// Statements that are needed for the 'condition' in a while also
|
||||
// need to be inserted on the back-edge to the loop header.
|
||||
// 'incsp' is just right palce to do this
|
||||
if (m_inWhileCondp) m_inWhileCondp->addIncsp(assignp->cloneTree(false));
|
||||
}
|
||||
|
||||
// Replace node with VarRef to new Var
|
||||
|
|
@ -173,24 +168,9 @@ class PremitVisitor final : public VNVisitor {
|
|||
if (stmtp->user1SetOnce()) return; \
|
||||
VL_RESTORER(m_assignLhs); \
|
||||
VL_RESTORER(m_stmtp); \
|
||||
VL_RESTORER(m_inWhileCondp); \
|
||||
m_assignLhs = false; \
|
||||
m_stmtp = stmtp; \
|
||||
m_inWhileCondp = nullptr
|
||||
m_stmtp = stmtp;
|
||||
|
||||
void visit(AstWhile* nodep) override {
|
||||
UINFO(4, " WHILE " << nodep);
|
||||
// cppcheck-suppress shadowVariable // Also restored below
|
||||
START_STATEMENT_OR_RETURN(nodep);
|
||||
{
|
||||
// cppcheck-suppress shadowVariable // Also restored above
|
||||
VL_RESTORER(m_inWhileCondp);
|
||||
m_inWhileCondp = nodep;
|
||||
iterateAndNextNull(nodep->condp());
|
||||
}
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
}
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
START_STATEMENT_OR_RETURN(nodep);
|
||||
|
||||
|
|
|
|||
|
|
@ -1530,12 +1530,15 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstNode* const stmtsp = iterVarp;
|
||||
stmtsp->addNext(
|
||||
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, new AstConst{fl, 0}});
|
||||
stmtsp->addNext(
|
||||
new AstWhile{fl, new AstLt{fl, new AstVarRef{fl, iterVarp, VAccess::READ}, sizep},
|
||||
new AstAssign{fl, setp, rhsp},
|
||||
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
|
||||
new AstAdd{fl, new AstConst{fl, 1},
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}}}});
|
||||
|
||||
AstLoop* const loopp = new AstLoop{fl};
|
||||
stmtsp->addNext(loopp);
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
fl, loopp, new AstLt{fl, new AstVarRef{fl, iterVarp, VAccess::READ}, sizep}});
|
||||
loopp->addStmtsp(new AstAssign{fl, setp, rhsp});
|
||||
loopp->addStmtsp(new AstAssign{
|
||||
fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
|
||||
new AstAdd{fl, new AstConst{fl, 1}, new AstVarRef{fl, iterVarp, VAccess::READ}}});
|
||||
return new AstBegin{fl, "", stmtsp, true};
|
||||
}
|
||||
static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) {
|
||||
|
|
|
|||
|
|
@ -102,11 +102,13 @@ class ReloopVisitor final : public VNVisitor {
|
|||
AstNode* const incp = new AstAssign{
|
||||
fl, new AstVarRef{fl, itp, VAccess::WRITE},
|
||||
new AstAdd{fl, new AstConst{fl, 1}, new AstVarRef{fl, itp, VAccess::READ}}};
|
||||
AstWhile* const whilep = new AstWhile{fl, condp, nullptr, incp};
|
||||
initp->addNext(whilep);
|
||||
AstLoop* const loopp = new AstLoop{fl};
|
||||
loopp->addStmtsp(new AstLoopTest{fl, loopp, condp});
|
||||
initp->addNext(loopp);
|
||||
itp->AstNode::addNext(initp);
|
||||
bodyp->replaceWith(itp);
|
||||
whilep->addStmtsp(bodyp);
|
||||
loopp->addStmtsp(bodyp);
|
||||
loopp->addStmtsp(incp);
|
||||
|
||||
// Replace constant index with new loop index
|
||||
AstNodeExpr* const offsetp
|
||||
|
|
@ -122,7 +124,7 @@ class ReloopVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(rbitp->deleteTree(), lbitp);
|
||||
}
|
||||
UINFOTREE(9, initp, "", "new");
|
||||
UINFOTREE(9, whilep, "", "new");
|
||||
UINFOTREE(9, loopp, "", "new");
|
||||
|
||||
// Remove remaining assigns
|
||||
for (AstNodeAssign* assp : m_mgAssignps) {
|
||||
|
|
|
|||
|
|
@ -263,8 +263,9 @@ EvalLoop createEvalLoop(
|
|||
|
||||
// The loop
|
||||
{
|
||||
AstWhile* const loopp
|
||||
= new AstWhile{flp, new AstVarRef{flp, continueFlagp, VAccess::READ}};
|
||||
AstNodeExpr* const condp = new AstVarRef{flp, continueFlagp, VAccess::READ};
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, condp});
|
||||
|
||||
// Check the iteration limit (aborts if exceeded)
|
||||
loopp->addStmtsp(checkIterationLimit(netlistp, name, counterp, dumpFuncp));
|
||||
|
|
@ -451,14 +452,12 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
|
|||
if (VN_IS(procp, Always)) {
|
||||
subFuncp->slow(false);
|
||||
FileLine* const flp = procp->fileline();
|
||||
bodyp = new AstWhile{
|
||||
flp,
|
||||
// If we change to use exceptions to handle finish/stop,
|
||||
// this can get removed
|
||||
new AstCExpr{flp,
|
||||
"VL_LIKELY(!vlSymsp->_vm_contextp__->gotFinish())", 1,
|
||||
true},
|
||||
bodyp};
|
||||
AstNodeExpr* const condp = new AstCExpr{
|
||||
flp, "VL_LIKELY(!vlSymsp->_vm_contextp__->gotFinish())", 1, true};
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, condp});
|
||||
loopp->addStmtsp(bodyp);
|
||||
bodyp = loopp;
|
||||
}
|
||||
}
|
||||
subFuncp->addStmtsp(bodyp);
|
||||
|
|
|
|||
|
|
@ -195,9 +195,8 @@ private:
|
|||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
unsupportedWriteToVirtIface(nodep->condp(), "loop condition");
|
||||
unsupportedWriteToVirtIface(nodep->incsp(), "loop increment statement");
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
|
|
@ -211,6 +210,9 @@ private:
|
|||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
unsupportedWriteToVirtIface(nodep->condp(), "loop condition");
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
|
|
|
|||
108
src/V3Simulate.h
108
src/V3Simulate.h
|
|
@ -141,7 +141,7 @@ private:
|
|||
int m_instrCount; ///< Number of nodes
|
||||
int m_dataCount; ///< Bytes of data
|
||||
int m_recurseCount = 0; // Now deep in current recursion
|
||||
AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from
|
||||
AstNode* m_jumptargetp = nullptr; // AstJumpBlock/AstLoop we are branching over
|
||||
// Simulating:
|
||||
// Allocators for constants of various data types
|
||||
std::unordered_map<const AstNodeDType*, ConstAllocator> m_constps;
|
||||
|
|
@ -409,7 +409,7 @@ private:
|
|||
}
|
||||
|
||||
// True if current node might be jumped over - all visitors must call this up front
|
||||
bool jumpingOver() const { return m_jumpp; }
|
||||
bool jumpingOver() const { return m_jumptargetp; }
|
||||
void assignOutValue(const AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) {
|
||||
if (VN_IS(nodep, AssignDly)) {
|
||||
// Don't do setValue, as value isn't yet visible to following statements
|
||||
|
|
@ -1056,9 +1056,9 @@ private:
|
|||
void visit(AstJumpBlock* nodep) override {
|
||||
if (jumpingOver()) return;
|
||||
iterateChildrenConst(nodep);
|
||||
if (m_jumpp && m_jumpp->blockp() == nodep) {
|
||||
if (m_jumptargetp == nodep) {
|
||||
UINFO(5, " JUMP DONE " << nodep);
|
||||
m_jumpp = nullptr;
|
||||
m_jumptargetp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
|
|
@ -1066,9 +1066,55 @@ private:
|
|||
checkNodeInfo(nodep);
|
||||
if (!m_checkOnly) {
|
||||
UINFO(5, " JUMP GO " << nodep);
|
||||
// Should be back at the JumpBlock and clear m_jumpp before another JumpGo
|
||||
UASSERT_OBJ(!m_jumpp, nodep, "Jump inside jump");
|
||||
m_jumpp = nodep;
|
||||
UASSERT_OBJ(!m_jumptargetp, nodep, "Jump inside jump");
|
||||
m_jumptargetp = nodep->blockp();
|
||||
}
|
||||
}
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
if (jumpingOver()) return;
|
||||
UINFO(5, " LOOP " << nodep);
|
||||
// Doing lots of loops is slow, so only for parameters
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
}
|
||||
checkNodeInfo(nodep);
|
||||
if (m_checkOnly) {
|
||||
iterateChildrenConst(nodep);
|
||||
return;
|
||||
}
|
||||
if (!optimizable()) return;
|
||||
|
||||
int loops = 0;
|
||||
while (true) {
|
||||
UINFO(5, " LOOP-ITER " << nodep);
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
if (jumpingOver()) break;
|
||||
|
||||
// Prep for next loop
|
||||
if (loops++ > v3Global.opt.unrollCountAdjusted(nodep->unroll(), m_params, true)) {
|
||||
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an"
|
||||
"infinite loop, or use /*verilator unroll_full*/, or "
|
||||
"set --unroll-count above "
|
||||
+ cvtToStr(loops));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_jumptargetp == nodep) {
|
||||
UINFO(5, " LOOP TEST DONE " << nodep);
|
||||
m_jumptargetp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateConst(nodep->condp());
|
||||
if (!m_checkOnly && optimizable() && fetchConst(nodep->condp())->num().isEqZero()) {
|
||||
UINFO(5, " LOOP TEST GO " << nodep);
|
||||
UASSERT_OBJ(!m_jumptargetp, nodep, "Jump inside jump");
|
||||
m_jumptargetp = nodep->loopp();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1082,46 +1128,6 @@ private:
|
|||
}
|
||||
checkNodeInfo(nodep);
|
||||
}
|
||||
|
||||
void visit(AstWhile* nodep) override {
|
||||
// Doing lots of Whiles is slow, so only for parameters
|
||||
if (jumpingOver()) return;
|
||||
UINFO(5, " WHILE " << nodep);
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
}
|
||||
checkNodeInfo(nodep);
|
||||
if (m_checkOnly) {
|
||||
iterateChildrenConst(nodep);
|
||||
} else if (optimizable()) {
|
||||
int loops = 0;
|
||||
while (true) {
|
||||
UINFO(5, " WHILE-ITER " << nodep);
|
||||
iterateAndNextConstNull(nodep->condp());
|
||||
if (jumpingOver()) break;
|
||||
if (!optimizable()) break;
|
||||
if (!fetchConst(nodep->condp())->num().isNeqZero()) { //
|
||||
break;
|
||||
}
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
if (jumpingOver()) break;
|
||||
iterateAndNextConstNull(nodep->incsp());
|
||||
if (jumpingOver()) break;
|
||||
|
||||
// Prep for next loop
|
||||
if (loops++
|
||||
> v3Global.opt.unrollCountAdjusted(nodep->unrollFull(), m_params, true)) {
|
||||
clearOptimizable(nodep, "Loop unrolling took too long; probably this is an"
|
||||
"infinite loop, or use /*verilator unroll_full*/, or "
|
||||
"set --unroll-count above "
|
||||
+ cvtToStr(loops));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
if (jumpingOver()) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
|
|
@ -1375,7 +1381,7 @@ private:
|
|||
}
|
||||
void mainGuts(AstNode* nodep) {
|
||||
iterateConst(nodep);
|
||||
UASSERT_OBJ(!m_jumpp, m_jumpp, "JumpGo branched to label that wasn't found");
|
||||
UASSERT_OBJ(!m_jumptargetp, m_jumptargetp, "Jump target was not found");
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1395,7 +1401,7 @@ public:
|
|||
m_isCoverage = false;
|
||||
m_instrCount = 0;
|
||||
m_dataCount = 0;
|
||||
m_jumpp = nullptr;
|
||||
m_jumptargetp = nullptr;
|
||||
|
||||
AstNode::user1ClearTree();
|
||||
m_varAux.clear();
|
||||
|
|
@ -1409,10 +1415,6 @@ public:
|
|||
setMode(true /*scoped*/, false /*checking*/, false /*params*/);
|
||||
mainGuts(nodep);
|
||||
}
|
||||
void mainCheckTree(AstNode* nodep) {
|
||||
setMode(false /*scoped*/, true /*checking*/, false /*params*/);
|
||||
mainGuts(nodep);
|
||||
}
|
||||
void mainParamEmulate(AstNode* nodep) {
|
||||
setMode(false /*scoped*/, false /*checking*/, true /*params*/);
|
||||
mainGuts(nodep);
|
||||
|
|
|
|||
|
|
@ -338,8 +338,7 @@ protected:
|
|||
void visit(AstAlways* nodep) override = 0;
|
||||
void visit(AstNodeIf* nodep) override = 0;
|
||||
|
||||
// We don't do AstWhile loops, due to the standard question
|
||||
// of what is before vs. after
|
||||
// We don't do AstLoop, due to the standard question of what is before vs. after
|
||||
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
VL_RESTORER(m_inDly);
|
||||
|
|
|
|||
|
|
@ -1610,21 +1610,6 @@ class TaskVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
// Special, as statements need to be put in different places
|
||||
{
|
||||
// Conditions will create a StmtExpr
|
||||
// Leave m_instStmtp = null, so will assert if not
|
||||
iterateAndNextNull(nodep->condp());
|
||||
}
|
||||
{
|
||||
// Body insert just before themselves
|
||||
VL_RESTORER(m_insStmtp);
|
||||
m_insStmtp = nullptr; // First thing should be new statement
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
}
|
||||
}
|
||||
void visit(AstNodeForeach* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc(
|
||||
"Foreach statements should have been converted to while statements in V3Begin.cpp");
|
||||
|
|
|
|||
|
|
@ -944,9 +944,11 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
// Create the trigger eval loop, which will await the evaluation step and check the
|
||||
// trigger
|
||||
AstWhile* const loopp = new AstWhile{
|
||||
flp, new AstLogNot{flp, new AstVarRef{flp, trigvscp, VAccess::READ}},
|
||||
awaitEvalp->makeStmt()};
|
||||
AstNodeExpr* const condp
|
||||
= new AstLogNot{flp, new AstVarRef{flp, trigvscp, VAccess::READ}};
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, condp});
|
||||
loopp->addStmtsp(awaitEvalp->makeStmt());
|
||||
// Put pre updates before the trigger check and assignment
|
||||
for (AstNodeStmt* const stmtp : senResults.m_preUpdates) loopp->addStmtsp(stmtp);
|
||||
// Then the trigger check and assignment
|
||||
|
|
@ -1206,7 +1208,9 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
flp, new AstSenItem{flp, VEdgeType::ET_CHANGED, condp->cloneTree(false)}},
|
||||
nullptr};
|
||||
controlp->user2(true); // Commit immediately
|
||||
AstWhile* const loopp = new AstWhile{flp, new AstLogNot{flp, condp}, controlp};
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
|
||||
loopp->addStmtsp(controlp);
|
||||
if (stmtsp) AstNode::addNext<AstNode, AstNode>(loopp, stmtsp);
|
||||
nodep->replaceWith(loopp);
|
||||
}
|
||||
|
|
|
|||
769
src/V3Unroll.cpp
769
src/V3Unroll.cpp
|
|
@ -1,6 +1,6 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add temporaries, such as for unroll nodes
|
||||
// DESCRIPTION: Verilator: Loop unrolling
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
|
|
@ -13,14 +13,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Unroll's Transformations:
|
||||
// Note is called twice. Once on modules for GenFor unrolling,
|
||||
// Again after V3Scope for normal for loop unrolling.
|
||||
//
|
||||
// Each module:
|
||||
// Look for "FOR" loops and unroll them if <= 32 loops.
|
||||
// (Eventually, a better way would be to simulate the entire loop; ala V3Table.)
|
||||
// Convert remaining FORs to WHILEs
|
||||
// Unroll AstLoopStmts
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
|
@ -29,489 +23,374 @@
|
|||
#include "V3Unroll.h"
|
||||
|
||||
#include "V3Const.h"
|
||||
#include "V3Simulate.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Unroll state, as a visitor of each AstNode
|
||||
// Statistics tracking
|
||||
|
||||
class UnrollVisitor final : public VNVisitor {
|
||||
// STATE - across all visitors
|
||||
AstVar* m_forVarp; // Iterator variable
|
||||
const AstVarScope* m_forVscp; // Iterator variable scope (nullptr for generate pass)
|
||||
const AstNode* m_ignoreIncp; // Increment node to ignore
|
||||
bool m_varModeCheck; // Just checking RHS assignments
|
||||
bool m_varAssignHit; // Assign var hit
|
||||
bool m_forkHit; // Fork hit
|
||||
bool m_generate; // Expand single generate For loop
|
||||
string m_beginName; // What name to give begin iterations
|
||||
// STATE - Statistic tracking
|
||||
VDouble0 m_statLoops; // Statistic tracking
|
||||
VDouble0 m_statIters; // Statistic tracking
|
||||
struct UnrollStats final {
|
||||
class Stat final {
|
||||
size_t m_value = 0; // Statistics value
|
||||
const char* const m_name; // Name for stats file and UDEBUG
|
||||
|
||||
public:
|
||||
Stat(const char* const name)
|
||||
: m_name{name} {}
|
||||
~Stat() { V3Stats::addStat("Optimizations, Loop unrolling, "s + m_name, m_value); }
|
||||
const char* name() const { return m_name; }
|
||||
Stat& operator++() {
|
||||
++m_value;
|
||||
return *this;
|
||||
}
|
||||
Stat& operator+=(size_t v) {
|
||||
m_value += v;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// STATE - statistics
|
||||
Stat m_nFailCall{"Failed - non-inlined call"};
|
||||
Stat m_nFailCondition{"Failed - unknown loop condition"};
|
||||
Stat m_nFailFork{"Failed - contains fork"};
|
||||
Stat m_nFailInfinite{"Failed - infinite loop"};
|
||||
Stat m_nFailNestedLoopTest{"Failed - loop test in sub-statement"};
|
||||
Stat m_nFailTimingControl{"Failed - contains timing control"};
|
||||
Stat m_nFailUnrollCount{"Failed - reached --unroll-count"};
|
||||
Stat m_nFailUnrollStmts{"Failed - reached --unroll-stmts"};
|
||||
Stat m_nPragmaDisabled{"Pragma unroll_disable"};
|
||||
Stat m_nUnrolledLoops{"Unrolled loops"};
|
||||
Stat m_nUnrolledIters{"Unrolled iterations"};
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Unroll one AstLoop
|
||||
|
||||
class UnrollOneVisitor final : VNVisitor {
|
||||
// NODE STATE
|
||||
// AstVarScope::user1p() AstConst: Value of this AstVarScope
|
||||
const VNUser1InUse m_user1InUse;
|
||||
|
||||
// STATE
|
||||
UnrollStats& m_stats; // Statistics tracking
|
||||
AstLoop* const m_loopp; // The loop we are trying to unroll
|
||||
AstNode* m_stmtsp = nullptr; // Resulting unrolled statement
|
||||
size_t m_unrolledSize = 0; // Number of nodes in unrolled loop
|
||||
// Temporary block needed for iteration of cloned statements
|
||||
AstBegin* const m_wrapp = new AstBegin{m_loopp->fileline(), "[EditWrapper]", nullptr, false};
|
||||
const bool m_unrollFull = m_loopp->unroll().isSetTrue(); // Completely unroll the loop?
|
||||
bool m_ok = true; // Unrolling successful so far, gave up if false
|
||||
|
||||
// METHODS
|
||||
void replaceVarRef(AstNode* bodyp, AstNode* varValuep) {
|
||||
// Replace all occurances of loop variable in bodyp and next
|
||||
bodyp->foreachAndNext([this, varValuep](AstVarRef* refp) {
|
||||
if (refp->varp() == m_forVarp && refp->varScopep() == m_forVscp
|
||||
&& refp->access().isReadOnly()) {
|
||||
AstNode* const newconstp = varValuep->cloneTree(false);
|
||||
refp->replaceWith(newconstp);
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
});
|
||||
void cantUnroll(AstNode* nodep, UnrollStats::Stat& stat) {
|
||||
m_ok = false;
|
||||
++stat;
|
||||
UINFO(4, " Can't Unroll: " << stat.name() << " :" << nodep);
|
||||
}
|
||||
|
||||
bool cantUnroll(AstNode* nodep, const char* reason) const {
|
||||
if (m_generate)
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason);
|
||||
UINFO(4, " Can't Unroll: " << reason << " :" << nodep);
|
||||
// UINFOTREE(9, nodep, "", "cant");
|
||||
V3Stats::addStatSum("Unrolling gave up, "s + reason, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bodySizeOverRecurse(AstNode* nodep, int& bodySize, int bodyLimit) {
|
||||
if (!nodep) return false;
|
||||
bodySize++;
|
||||
// Exit once exceeds limits, rather than always total
|
||||
// so don't go O(n^2) when can't unroll
|
||||
if (bodySize > bodyLimit) return true;
|
||||
if (bodySizeOverRecurse(nodep->op1p(), bodySize, bodyLimit)) return true;
|
||||
if (bodySizeOverRecurse(nodep->op2p(), bodySize, bodyLimit)) return true;
|
||||
if (bodySizeOverRecurse(nodep->op3p(), bodySize, bodyLimit)) return true;
|
||||
if (bodySizeOverRecurse(nodep->op4p(), bodySize, bodyLimit)) return true;
|
||||
// Tail recurse.
|
||||
return bodySizeOverRecurse(nodep->nextp(), bodySize, bodyLimit);
|
||||
}
|
||||
|
||||
bool forUnrollCheck(
|
||||
AstNode* const nodep,
|
||||
const VOptionBool& unrollFull, // Pragma unroll_full, unroll_disable
|
||||
AstNode* const initp, // Maybe under nodep (no nextp), or standalone (ignore nextp)
|
||||
AstNode* condp,
|
||||
AstNode* const incp, // Maybe under nodep or in bodysp
|
||||
AstNode* bodysp) {
|
||||
// To keep the IF levels low, we return as each test fails.
|
||||
UINFO(4, " FOR Check " << nodep);
|
||||
if (initp) UINFO(6, " Init " << initp);
|
||||
if (condp) UINFO(6, " Cond " << condp);
|
||||
if (incp) UINFO(6, " Inc " << incp);
|
||||
|
||||
if (unrollFull.isSetFalse()) return cantUnroll(nodep, "pragma unroll_disable");
|
||||
|
||||
// Initial value check
|
||||
AstAssign* const initAssp = VN_CAST(initp, Assign);
|
||||
if (!initAssp) return cantUnroll(nodep, "no initial assignment");
|
||||
UASSERT_OBJ(!(initp->nextp() && initp->nextp() != nodep), nodep,
|
||||
"initial assignment shouldn't be a list");
|
||||
if (!VN_IS(initAssp->lhsp(), VarRef)) {
|
||||
return cantUnroll(nodep, "no initial assignment to simple variable");
|
||||
}
|
||||
//
|
||||
// Condition check
|
||||
UASSERT_OBJ(!condp->nextp(), nodep, "conditional shouldn't be a list");
|
||||
//
|
||||
// Assignment of next value check
|
||||
const AstAssign* const incAssp = VN_CAST(incp, Assign);
|
||||
if (!incAssp) return cantUnroll(nodep, "no increment assignment");
|
||||
if (incAssp->nextp()) return cantUnroll(nodep, "multiple increments");
|
||||
|
||||
m_forVarp = VN_AS(initAssp->lhsp(), VarRef)->varp();
|
||||
m_forVscp = VN_AS(initAssp->lhsp(), VarRef)->varScopep();
|
||||
if (VN_IS(nodep, GenFor) && !m_forVarp->isGenVar()) {
|
||||
nodep->v3error("Non-genvar used in generate for: " << m_forVarp->prettyNameQ());
|
||||
} else if (!VN_IS(nodep, GenFor) && m_forVarp->isGenVar()) {
|
||||
// Likely impossible as V3LinkResolve will earlier throw bad genvar use error
|
||||
nodep->v3error("Genvar not legal in non-generate for" // LCOV_EXCL_LINE
|
||||
" (IEEE 1800-2023 27.4): "
|
||||
<< m_forVarp->prettyNameQ());
|
||||
}
|
||||
if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change
|
||||
|
||||
// This check shouldn't be needed when using V3Simulate
|
||||
// however, for repeat loops, the loop variable is auto-generated
|
||||
// and the initp statements will reference a variable outside of the initp scope
|
||||
// alas, failing to simulate.
|
||||
const AstConst* const constInitp = VN_CAST(initAssp->rhsp(), Const);
|
||||
if (!constInitp) return cantUnroll(nodep, "non-constant initializer");
|
||||
|
||||
//
|
||||
// Now, make sure there's no assignment to this variable in the loop
|
||||
m_varModeCheck = true;
|
||||
m_varAssignHit = false;
|
||||
m_forkHit = false;
|
||||
m_ignoreIncp = incp;
|
||||
iterateAndNextNull(bodysp);
|
||||
iterateAndNextNull(incp);
|
||||
m_varModeCheck = false;
|
||||
m_ignoreIncp = nullptr;
|
||||
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
||||
|
||||
if (m_forkHit) return cantUnroll(nodep, "fork inside loop");
|
||||
|
||||
//
|
||||
if (m_forVscp) {
|
||||
UINFO(8, " Loop Variable: " << m_forVscp);
|
||||
} else {
|
||||
UINFO(8, " Loop Variable: " << m_forVarp);
|
||||
}
|
||||
UINFOTREE(9, nodep, "", "for");
|
||||
|
||||
if (!m_generate) {
|
||||
const AstAssign* const incpAssign = VN_AS(incp, Assign);
|
||||
if (!canSimulate(incpAssign->rhsp())) {
|
||||
return cantUnroll(incp, "Unable to simulate increment");
|
||||
}
|
||||
if (!canSimulate(condp)) return cantUnroll(condp, "Unable to simulate condition");
|
||||
|
||||
// Check whether to we actually want to try and unroll.
|
||||
int loops;
|
||||
const int limit = v3Global.opt.unrollCountAdjusted(unrollFull, m_generate, false);
|
||||
if (!countLoops(initAssp, condp, incp, limit, loops)) {
|
||||
return cantUnroll(nodep, "Unable to simulate loop");
|
||||
}
|
||||
|
||||
// Less than 10 statements in the body?
|
||||
if (!unrollFull.isSetTrue()) {
|
||||
int bodySize = 0;
|
||||
int bodyLimit = v3Global.opt.unrollStmts();
|
||||
if (loops > 0) bodyLimit = v3Global.opt.unrollStmts() / loops;
|
||||
if (bodySizeOverRecurse(bodysp, bodySize /*ref*/, bodyLimit)
|
||||
|| bodySizeOverRecurse(incp, bodySize /*ref*/, bodyLimit)) {
|
||||
return cantUnroll(nodep, "too many statements");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally, we can do it
|
||||
if (!forUnroller(nodep, unrollFull, initAssp, condp, incp, bodysp)) {
|
||||
return cantUnroll(nodep, "Unable to unroll loop");
|
||||
}
|
||||
VL_DANGLING(nodep);
|
||||
// Cleanup
|
||||
return true;
|
||||
}
|
||||
|
||||
bool canSimulate(AstNode* nodep) {
|
||||
SimulateVisitor simvis;
|
||||
AstNode* clonep = nodep->cloneTree(true);
|
||||
simvis.mainCheckTree(clonep);
|
||||
VL_DO_CLEAR(pushDeletep(clonep), clonep = nullptr);
|
||||
return simvis.optimizable();
|
||||
}
|
||||
|
||||
bool simulateTree(AstNode* nodep, const V3Number* loopValue, AstNode* dtypep,
|
||||
V3Number& outNum) {
|
||||
AstNode* clonep = nodep->cloneTree(true);
|
||||
UASSERT_OBJ(clonep, nodep, "Failed to clone tree");
|
||||
if (loopValue) {
|
||||
AstConst* varValuep = new AstConst{nodep->fileline(), *loopValue};
|
||||
// Iteration requires a back, so put under temporary node
|
||||
AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false};
|
||||
replaceVarRef(tempp->stmtsp(), varValuep);
|
||||
clonep = tempp->stmtsp()->unlinkFrBackWithNext();
|
||||
VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr);
|
||||
VL_DO_DANGLING(pushDeletep(varValuep), varValuep);
|
||||
}
|
||||
SimulateVisitor simvis;
|
||||
simvis.mainParamEmulate(clonep);
|
||||
if (!simvis.optimizable()) {
|
||||
UINFO(4, "Unable to simulate");
|
||||
UINFOTREE(9, nodep, "", "_simtree");
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return false;
|
||||
}
|
||||
// Fetch the result
|
||||
V3Number* resp = simvis.fetchNumberNull(clonep);
|
||||
if (!resp) {
|
||||
UINFO(3, "No number returned from simulation");
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return false;
|
||||
}
|
||||
// Patch up datatype
|
||||
if (dtypep) {
|
||||
AstConst new_con{clonep->fileline(), *resp};
|
||||
new_con.dtypeFrom(dtypep);
|
||||
outNum = new_con.num();
|
||||
outNum.isSigned(dtypep->isSigned());
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return true;
|
||||
}
|
||||
outNum = *resp;
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool countLoops(AstAssign* initp, AstNode* condp, AstNode* incp, int max, int& outLoopsr) {
|
||||
outLoopsr = 0;
|
||||
V3Number loopValue{initp};
|
||||
if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { //
|
||||
return false;
|
||||
}
|
||||
while (true) {
|
||||
V3Number res{initp};
|
||||
if (!simulateTree(condp, &loopValue, nullptr, res)) { //
|
||||
return false;
|
||||
}
|
||||
if (!res.isEqOne()) break;
|
||||
|
||||
outLoopsr++;
|
||||
|
||||
// Run inc
|
||||
AstAssign* const incpass = VN_AS(incp, Assign);
|
||||
V3Number newLoopValue{initp};
|
||||
if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) {
|
||||
return false;
|
||||
}
|
||||
loopValue.opAssign(newLoopValue);
|
||||
if (outLoopsr > max) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool forUnroller(AstNode* nodep, const VOptionBool& unrollFull, AstAssign* initp,
|
||||
AstNode* condp, AstNode* incp, AstNode* bodysp) {
|
||||
UINFO(9, "forUnroller " << nodep);
|
||||
V3Number loopValue{nodep};
|
||||
if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { //
|
||||
return false;
|
||||
}
|
||||
AstNode* stmtsp = nullptr;
|
||||
if (initp) {
|
||||
initp->unlinkFrBack(); // Always a single statement; nextp() may be nodep
|
||||
// Don't add to list, we do it once, and setting loop index isn't
|
||||
// needed if we have > 1 loop, as we're constant propagating it
|
||||
pushDeletep(initp); // Always cloned below.
|
||||
}
|
||||
if (bodysp) {
|
||||
bodysp->unlinkFrBackWithNext();
|
||||
stmtsp = AstNode::addNext(stmtsp, bodysp); // Maybe null if no body
|
||||
}
|
||||
if (incp && !VN_IS(nodep, GenFor)) { // Generates don't need to increment loop index
|
||||
incp->unlinkFrBackWithNext();
|
||||
stmtsp = AstNode::addNext(stmtsp, incp); // Maybe null if no body
|
||||
}
|
||||
// Mark variable to disable some later warnings
|
||||
m_forVarp->usedLoopIdx(true);
|
||||
|
||||
++m_statLoops;
|
||||
AstNode* newbodysp = nullptr;
|
||||
if (initp && !m_generate) { // Set variable to initial value (may optimize away later)
|
||||
AstNode* clonep = initp->cloneTree(true);
|
||||
AstConst* varValuep = new AstConst{nodep->fileline(), loopValue};
|
||||
// Iteration requires a back, so put under temporary node
|
||||
AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false};
|
||||
replaceVarRef(clonep, varValuep);
|
||||
clonep = tempp->stmtsp()->unlinkFrBackWithNext();
|
||||
VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr);
|
||||
VL_DO_DANGLING(pushDeletep(varValuep), varValuep);
|
||||
newbodysp = clonep;
|
||||
}
|
||||
if (stmtsp) {
|
||||
pushDeletep(stmtsp); // Always cloned below.
|
||||
int times = 0;
|
||||
while (true) {
|
||||
UINFO(8, " Looping " << loopValue);
|
||||
V3Number res{nodep};
|
||||
if (!simulateTree(condp, &loopValue, nullptr, res)) {
|
||||
nodep->v3error("Loop unrolling failed.");
|
||||
// Returns false if the loop terminated (or we gave up)
|
||||
bool unrollOneIteration(AstNode* stmtsp) {
|
||||
// True if the loop contains at least one dependent AstLoopTest
|
||||
bool foundLoopTest = false;
|
||||
// Process one body statement at a time
|
||||
for (AstNode* stmtp = stmtsp; stmtp; stmtp = stmtp->nextp()) {
|
||||
// Check if this is a loop test - before substitution
|
||||
if (AstLoopTest* const testp = VN_CAST(stmtp, LoopTest)) {
|
||||
AstNode* const condp = V3Const::constifyEdit(testp->condp());
|
||||
if (condp->isZero()) {
|
||||
// Loop terminates
|
||||
return false;
|
||||
} else if (condp->isNeqZero()) {
|
||||
// Loop test is unconditionally true, ignore
|
||||
continue;
|
||||
}
|
||||
if (!res.isEqOne()) {
|
||||
break; // Done with the loop
|
||||
} else {
|
||||
// Replace iterator values with constant
|
||||
AstNode* oneloopp = stmtsp->cloneTree(true);
|
||||
AstConst* varValuep = new AstConst{nodep->fileline(), loopValue};
|
||||
if (oneloopp) {
|
||||
// Iteration requires a back, so put under temporary node
|
||||
AstBegin* const tempp
|
||||
= new AstBegin{oneloopp->fileline(), "[EditWrapper]", oneloopp, false};
|
||||
replaceVarRef(tempp->stmtsp(), varValuep);
|
||||
oneloopp = tempp->stmtsp()->unlinkFrBackWithNext();
|
||||
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
||||
}
|
||||
if (m_generate) {
|
||||
const string index = AstNode::encodeNumber(varValuep->toSInt());
|
||||
const string nname = m_beginName + "__BRA__" + index + "__KET__";
|
||||
oneloopp = new AstGenBlock{oneloopp->fileline(), nname, oneloopp, false};
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(varValuep), varValuep);
|
||||
if (newbodysp) {
|
||||
newbodysp->addNext(oneloopp);
|
||||
} else {
|
||||
newbodysp = oneloopp;
|
||||
}
|
||||
}
|
||||
|
||||
++m_statIters;
|
||||
const int limit
|
||||
= v3Global.opt.unrollCountAdjusted(unrollFull, m_generate, false);
|
||||
if (++times / 3 > limit) {
|
||||
nodep->v3error(
|
||||
"Loop unrolling took too long;"
|
||||
" probably this is an infinite loop, "
|
||||
" or use /*verilator unroll_full*/, or set --unroll-count above "
|
||||
<< times);
|
||||
break;
|
||||
}
|
||||
// Clone and iterate one body statement
|
||||
m_wrapp->addStmtsp(stmtp->cloneTree(false));
|
||||
iterateAndNextNull(m_wrapp->stmtsp());
|
||||
// Give up if failed
|
||||
if (!m_ok) return false;
|
||||
|
||||
// loopValue += valInc
|
||||
AstAssign* const incpass = VN_AS(incp, Assign);
|
||||
V3Number newLoopValue{nodep};
|
||||
if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) {
|
||||
nodep->v3error("Loop unrolling failed");
|
||||
// Add statements to unrolled body
|
||||
while (AstNode* const nodep = m_wrapp->stmtsp()) {
|
||||
// Check if we reached the size limit, unless full unrolling is requested
|
||||
if (!m_loopp->unroll().isSetTrue()) {
|
||||
m_unrolledSize += nodep->nodeCount();
|
||||
if (m_unrolledSize > static_cast<size_t>(v3Global.opt.unrollStmts())) {
|
||||
cantUnroll(m_loopp, m_stats.m_nFailUnrollStmts);
|
||||
return false;
|
||||
}
|
||||
loopValue.opAssign(newLoopValue);
|
||||
}
|
||||
|
||||
// Will be adding to results (or deleting)
|
||||
nodep->unlinkFrBack();
|
||||
|
||||
// If a LoopTest, check how it resolved
|
||||
if (AstLoopTest* const testp = VN_CAST(nodep, LoopTest)) {
|
||||
foundLoopTest = true;
|
||||
// Will not actually need it, nor any subsequent
|
||||
pushDeletep(testp);
|
||||
// Loop continues - add rest of statements
|
||||
if (testp->condp()->isNeqZero()) continue;
|
||||
// Won't need any of the trailing statements
|
||||
if (m_wrapp->stmtsp()) pushDeletep(m_wrapp->stmtsp()->unlinkFrBackWithNext());
|
||||
// Loop terminates
|
||||
if (testp->condp()->isZero()) return false;
|
||||
// Loop condition unknown - cannot unroll
|
||||
cantUnroll(testp->condp(), m_stats.m_nFailCondition);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add this statement to the result list
|
||||
m_stmtsp = AstNode::addNext(m_stmtsp, nodep);
|
||||
|
||||
// Check if terminated via JumpGo
|
||||
if (VN_IS(nodep, JumpGo)) {
|
||||
UASSERT_OBJ(!m_wrapp->stmtsp(), nodep, "Statements after JumpGo");
|
||||
// This JumpGo is going directly into the body of the unrolled loop.
|
||||
// A JumpGo always redirects to the end of an enclosing JumpBlock,
|
||||
// so this JumpGo must go outside the loop. The loop terminates.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!newbodysp) { // initp might have effects after the loop
|
||||
if (m_generate && initp) { // GENFOR(ASSIGN(...)) need to move under a new Initial
|
||||
newbodysp = new AstInitial{initp->fileline(), initp->cloneTree(true)};
|
||||
} else {
|
||||
newbodysp = initp ? initp->cloneTree(true) : nullptr;
|
||||
}
|
||||
// If there is no loop test in the body, give up, it's an infinite loop
|
||||
if (!foundLoopTest) {
|
||||
cantUnroll(m_loopp, m_stats.m_nFailInfinite);
|
||||
return false;
|
||||
}
|
||||
// Replace the FOR()
|
||||
if (newbodysp) {
|
||||
nodep->replaceWith(newbodysp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
if (newbodysp) UINFOTREE(9, newbodysp, "", "_new");
|
||||
// One iteration done, loop continues
|
||||
return true;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstWhile* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!m_varModeCheck) {
|
||||
// Constify before unroll call, as it may change what is underneath.
|
||||
if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change
|
||||
// Grab initial value
|
||||
AstNode* initp = nullptr; // Should be statement before the while.
|
||||
if (nodep->backp()->nextp() == nodep) initp = nodep->backp();
|
||||
if (initp) VL_DO_DANGLING(V3Const::constifyEdit(initp), initp);
|
||||
if (nodep->backp()->nextp() == nodep) initp = nodep->backp();
|
||||
// Grab assignment
|
||||
AstNode* incp = nullptr; // Should be last statement
|
||||
AstNode* stmtsp = nodep->stmtsp();
|
||||
if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp());
|
||||
// cppcheck-suppress duplicateCondition
|
||||
if (nodep->incsp()) {
|
||||
incp = nodep->incsp();
|
||||
} else {
|
||||
for (incp = nodep->stmtsp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
||||
if (incp) VL_DO_DANGLING(V3Const::constifyEdit(incp), incp);
|
||||
// Again, as may have changed
|
||||
stmtsp = nodep->stmtsp();
|
||||
for (incp = nodep->stmtsp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
||||
if (incp == stmtsp) stmtsp = nullptr;
|
||||
// Substitute all reads of bound variables with their value. If a write is
|
||||
// encountered, remove the binding and don't substitute that variable.
|
||||
// Returns false if we can't unroll
|
||||
bool process(AstNode* nodep) {
|
||||
UASSERT_OBJ(m_ok, nodep, "Should not call 'substituteCondVscp' if we gave up");
|
||||
if (!nodep) return true;
|
||||
// Variable references we should try to substitute
|
||||
std::vector<AstVarRef*> toSubstitute;
|
||||
// Iterate subtree
|
||||
nodep->foreach([&](AstNode* np) {
|
||||
// Failed earlier
|
||||
if (!m_ok) return;
|
||||
// Check for AstLoopTest
|
||||
if (AstLoopTest* const testp = VN_CAST(np, LoopTest)) {
|
||||
// Nested loop is OK, bail only if the nested LoopTest is for the current loop
|
||||
if (testp->loopp() == m_loopp) cantUnroll(np, m_stats.m_nFailNestedLoopTest);
|
||||
return;
|
||||
}
|
||||
// And check it
|
||||
if (forUnrollCheck(nodep, nodep->unrollFull(), initp, nodep->condp(), incp, stmtsp)) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstGenFor* nodep) override {
|
||||
UASSERT_OBJ(m_generate, nodep, "There should be no GenFor left when unrolling all");
|
||||
if (!m_varModeCheck) {
|
||||
// Constify before unroll call, as it may change what is underneath.
|
||||
if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change
|
||||
if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change
|
||||
if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); // incsp may change
|
||||
if (nodep->condp()->isZero()) {
|
||||
// We don't need to do any loops. Remove the GenFor,
|
||||
// Genvar's don't care about any initial assignments.
|
||||
//
|
||||
// Note normal For's can't do exactly this deletion, as
|
||||
// we'd need to initialize the variable to the initial
|
||||
// condition, but they'll become while's which can be
|
||||
// deleted by V3Const.
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
} else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nodep->condp(),
|
||||
nodep->incsp(), nodep->itemsp())) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement
|
||||
} else {
|
||||
nodep->v3error("For loop doesn't have genvar index, or is malformed");
|
||||
// We will die, do it gracefully
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
// Check for AstFork - can't unroll
|
||||
if (VN_IS(np, Fork)) {
|
||||
cantUnroll(np, m_stats.m_nFailFork);
|
||||
return;
|
||||
}
|
||||
// Check for calls - can't unroll if not pure might modify bindings
|
||||
if (AstNodeCCall* const callp = VN_CAST(np, NodeCCall)) {
|
||||
if (!callp->isPure()) {
|
||||
cantUnroll(np, m_stats.m_nFailCall);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check for timing control - can't unroll, might modify bindings
|
||||
if (np->isTimingControl()) {
|
||||
cantUnroll(np, m_stats.m_nFailTimingControl);
|
||||
return;
|
||||
}
|
||||
// Process variable references
|
||||
AstVarRef* const refp = VN_CAST(np, VarRef);
|
||||
if (!refp) return;
|
||||
// Ignore if the referenced variable has no binding
|
||||
AstConst* const valp = VN_AS(refp->varScopep()->user1p(), Const);
|
||||
if (!valp) return;
|
||||
// If writen, remove the binding
|
||||
if (refp->access().isWriteOrRW()) {
|
||||
refp->varScopep()->user1p(nullptr);
|
||||
return;
|
||||
}
|
||||
// Otherwise add it to the list of variables to substitute
|
||||
toSubstitute.push_back(refp);
|
||||
});
|
||||
// Give up if we have to
|
||||
if (!m_ok) return false;
|
||||
// Actually substitute the variables that still have bindings
|
||||
for (AstVarRef* const refp : toSubstitute) {
|
||||
// Pick up bound value
|
||||
AstConst* const valp = VN_AS(refp->varScopep()->user1p(), Const);
|
||||
// Binding might have been removed after adding to 'toSubstitute'
|
||||
if (!valp) continue;
|
||||
// Substitute it
|
||||
refp->replaceWith(valp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (m_varModeCheck && nodep->varp() == m_forVarp && nodep->varScopep() == m_forVscp
|
||||
&& nodep->access().isWriteOrRW()) {
|
||||
UINFO(8, " Itervar assigned to: " << nodep);
|
||||
m_varAssignHit = true;
|
||||
// CONSTRUCTOR
|
||||
UnrollOneVisitor(UnrollStats& stats, AstLoop* loopp)
|
||||
: m_stats{stats}
|
||||
, m_loopp{loopp} {
|
||||
UASSERT_OBJ(!loopp->contsp(), loopp, "'contsp' only used before LinkJump");
|
||||
// Do not unroll if we are told not to
|
||||
if (loopp->unroll().isSetFalse()) {
|
||||
cantUnroll(loopp, m_stats.m_nPragmaDisabled);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstFork* nodep) override {
|
||||
if (m_varModeCheck) {
|
||||
if (nodep->joinType().joinNone() || nodep->joinType().joinAny()) {
|
||||
// Forks are not allowed to unroll for loops, so we just set a flag
|
||||
m_forkHit = true;
|
||||
// Gather variable bindings from the preceding statements
|
||||
for (AstNode *succp = loopp, *currp = loopp->backp(); currp->nextp() == succp;
|
||||
succp = currp, currp = currp->backp()) {
|
||||
AstAssign* const assignp = VN_CAST(currp, Assign);
|
||||
if (!assignp) break;
|
||||
AstConst* const valp = VN_CAST(V3Const::constifyEdit(assignp->rhsp()), Const);
|
||||
if (!valp) break;
|
||||
AstVarRef* const lhsp = VN_CAST(assignp->lhsp(), VarRef);
|
||||
if (!lhsp) break;
|
||||
// Don't bind if volatile
|
||||
if (lhsp->varp()->isForced() || lhsp->varp()->isSigUserRWPublic()) continue;
|
||||
// Don't overwrite a later binding
|
||||
if (lhsp->varScopep()->user1p()) continue;
|
||||
// Set up the binding
|
||||
lhsp->varScopep()->user1p(valp);
|
||||
}
|
||||
// Attempt to unroll the loop
|
||||
const size_t iterLimit = v3Global.opt.unrollCountAdjusted(loopp->unroll(), false, false);
|
||||
size_t iterCount = 0;
|
||||
do {
|
||||
// Allow iterLimit + 1 iterations, which is consistent with the old behaviour
|
||||
// where 'do' loops used to be unrolled at least once, and while/for loops are
|
||||
// tested at the front on the last entry to the loop body
|
||||
if (iterCount > iterLimit) {
|
||||
cantUnroll(m_loopp, m_stats.m_nFailUnrollCount);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
++iterCount;
|
||||
} while (unrollOneIteration(loopp->stmtsp()));
|
||||
|
||||
if (m_ok) {
|
||||
++m_stats.m_nUnrolledLoops;
|
||||
m_stats.m_nUnrolledIters += iterCount;
|
||||
}
|
||||
}
|
||||
~UnrollOneVisitor() { VL_DO_DANGLING(m_wrapp->deleteTree(), m_wrapp); }
|
||||
|
||||
// VISIT - these are called for the statements directly in the loop body
|
||||
void visit(AstNode* nodep) override {
|
||||
if (m_varModeCheck && nodep == m_ignoreIncp) {
|
||||
// Ignore subtree that is the increment
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
if (!m_ok) return;
|
||||
// Generic body statement, just substitute
|
||||
process(nodep);
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
if (!m_ok) return;
|
||||
// If the condition is a ExprStmt, move it before the LoopTest
|
||||
if (AstExprStmt* const exprp = VN_CAST(nodep->condp(), ExprStmt)) {
|
||||
AstNode* const stmtsp = exprp->stmtsp()->unlinkFrBackWithNext();
|
||||
exprp->replaceWith(exprp->resultp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(exprp), exprp);
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
stmtsp->addNext(nodep);
|
||||
relinker.relink(stmtsp);
|
||||
return;
|
||||
}
|
||||
// Substitute the condition only, this is not a nested AstLoopTest
|
||||
process(nodep->condp());
|
||||
// Also simplify it, it will be checked later
|
||||
V3Const::constifyEdit(nodep->condp());
|
||||
}
|
||||
void visit(AstAssign* nodep) override {
|
||||
if (!m_ok) return;
|
||||
// Can't do it if delayed
|
||||
if (nodep->timingControlp()) {
|
||||
cantUnroll(nodep, m_stats.m_nFailTimingControl);
|
||||
return;
|
||||
}
|
||||
if (!process(nodep->rhsp())) return;
|
||||
// If a simple variable assignment, update the binding
|
||||
AstVarRef* const lhsp = VN_CAST(nodep->lhsp(), VarRef);
|
||||
AstConst* const valp = VN_CAST(V3Const::constifyEdit(nodep->rhsp()), Const);
|
||||
if (lhsp && valp && !lhsp->varp()->isForced() && !lhsp->varp()->isSigUserRWPublic()) {
|
||||
lhsp->varScopep()->user1p(valp);
|
||||
return;
|
||||
}
|
||||
// Otherwise just like a generic statement
|
||||
process(nodep->lhsp());
|
||||
}
|
||||
void visit(AstIf* nodep) override {
|
||||
if (!m_ok) return;
|
||||
if (!process(nodep->condp())) return;
|
||||
// If condition is constant, replce with the relevant branch
|
||||
if (AstConst* const condp = VN_CAST(V3Const::constifyEdit(nodep->condp()), Const)) {
|
||||
if (AstNode* const bodyp = condp->isNeqZero() ? nodep->thensp() : nodep->elsesp()) {
|
||||
nodep->addNextHere(bodyp->unlinkFrBackWithNext()); // This will be iterated next
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
// Otherwise just like a generic statement
|
||||
process(nodep);
|
||||
}
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
// Remove trailing dead code
|
||||
if (nodep->nextp()) pushDeletep(nodep->nextp()->unlinkFrBackWithNext());
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
UnrollVisitor() { init(false, ""); }
|
||||
~UnrollVisitor() override {
|
||||
V3Stats::addStatSum("Optimizations, Unrolled Loops", m_statLoops);
|
||||
V3Stats::addStatSum("Optimizations, Unrolled Iterations", m_statIters);
|
||||
}
|
||||
// METHODS
|
||||
void init(bool generate, const string& beginName) {
|
||||
m_forVarp = nullptr;
|
||||
m_forVscp = nullptr;
|
||||
m_ignoreIncp = nullptr;
|
||||
m_varModeCheck = false;
|
||||
m_varAssignHit = false;
|
||||
m_forkHit = false;
|
||||
m_generate = generate;
|
||||
m_beginName = beginName;
|
||||
}
|
||||
void process(AstNode* nodep, bool generate, const string& beginName) {
|
||||
init(generate, beginName);
|
||||
iterate(nodep);
|
||||
// Unroll the given loop. Returns the resulting statements and the number of
|
||||
// iterations unrolled (0 if unrolling failed);
|
||||
static std::pair<AstNode*, bool> apply(UnrollStats& stats, AstLoop* loopp) {
|
||||
UnrollOneVisitor visitor{stats, loopp};
|
||||
// If successfully unrolled, return the resulting list of statements - might be empty
|
||||
if (visitor.m_ok) return {visitor.m_stmtsp, true};
|
||||
// Otherwise delete intermediate results
|
||||
if (visitor.m_stmtsp) VL_DO_DANGLING(visitor.m_stmtsp->deleteTree(), visitor.m_stmtsp);
|
||||
return {nullptr, false};
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Unroll class functions
|
||||
// Unroll all AstLoop statements
|
||||
|
||||
UnrollStateful::UnrollStateful()
|
||||
: m_unrollerp{new UnrollVisitor} {}
|
||||
UnrollStateful::~UnrollStateful() { delete m_unrollerp; }
|
||||
class UnrollAllVisitor final : VNVisitor {
|
||||
// STATE - Statistic tracking
|
||||
UnrollStats m_stats;
|
||||
|
||||
void UnrollStateful::unrollGen(AstGenFor* nodep, const string& beginName) {
|
||||
UINFO(5, __FUNCTION__ << ": ");
|
||||
m_unrollerp->process(nodep, true, beginName);
|
||||
}
|
||||
// VISIT
|
||||
void visit(AstLoop* nodep) override {
|
||||
// Attempt to unroll this loop
|
||||
const std::pair<AstNode*, bool> pair = UnrollOneVisitor::apply(m_stats, nodep);
|
||||
|
||||
// If failed, carry on with nested loop
|
||||
if (!pair.second) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise replace the loop with the unrolled code - might be empty
|
||||
if (pair.first) {
|
||||
nodep->replaceWith(pair.first);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
// Iteration continues with the unrolled body
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
// CONSTRUCTOR
|
||||
UnrollAllVisitor(AstNetlist* netlistp) { iterate(netlistp); }
|
||||
|
||||
public:
|
||||
static void apply(AstNetlist* netlistp) { UnrollAllVisitor{netlistp}; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// V3Unroll class functions
|
||||
|
||||
void V3Unroll::unrollAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{
|
||||
UnrollVisitor visitor;
|
||||
visitor.process(nodep, false, "");
|
||||
} // Destruct before checking
|
||||
UnrollAllVisitor::apply(nodep);
|
||||
V3Global::dumpCheckGlobalTree("unroll", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Pre C-Emit stage changes
|
||||
// DESCRIPTION: Verilator: Loop unroller
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
|
|
@ -24,24 +24,24 @@
|
|||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
/// Unroller with saved state, so caller can determine when pushDelete's are executed.
|
||||
// Generate loop unroller
|
||||
|
||||
class UnrollVisitor;
|
||||
class UnrollGenVisitor;
|
||||
|
||||
class UnrollStateful final {
|
||||
class GenForUnroller final {
|
||||
// MEMBERS
|
||||
UnrollVisitor* const m_unrollerp;
|
||||
VL_UNCOPYABLE(UnrollStateful);
|
||||
UnrollGenVisitor* const m_unrollerp;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
UnrollStateful() VL_MT_DISABLED;
|
||||
~UnrollStateful() VL_MT_DISABLED;
|
||||
// CONSTRUCTOR
|
||||
GenForUnroller() VL_MT_DISABLED;
|
||||
~GenForUnroller() VL_MT_DISABLED;
|
||||
// METHODS
|
||||
void unrollGen(AstGenFor* nodep, const string& beginName) VL_MT_DISABLED;
|
||||
void unroll(AstGenFor* nodep, const std::string& beginName) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
// Loop statement unroller
|
||||
|
||||
class V3Unroll final {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,323 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Generate loop unrolling
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2025 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.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Unroll AstLoopStmts
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Const.h"
|
||||
#include "V3Simulate.h"
|
||||
#include "V3Unroll.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
//######################################################################
|
||||
// Unroll AstGenFor
|
||||
|
||||
class UnrollGenVisitor final : public VNVisitor {
|
||||
// STATE - across all visitors
|
||||
AstVar* m_forVarp = nullptr; // Iterator variable
|
||||
const AstNode* m_ignoreIncp = nullptr; // Increment node to ignore
|
||||
bool m_varModeCheck = false; // Just checking RHS assignments
|
||||
bool m_varAssignHit = false; // Assign var hit
|
||||
bool m_forkHit = false; // Fork hit
|
||||
std::string m_beginName; // What name to give begin iterations
|
||||
|
||||
// METHODS
|
||||
void replaceVarRef(AstNode* bodyp, AstNode* varValuep) {
|
||||
// Replace all occurances of loop variable in bodyp and next
|
||||
bodyp->foreachAndNext([this, varValuep](AstVarRef* refp) {
|
||||
if (refp->varp() == m_forVarp && refp->access().isReadOnly()) {
|
||||
AstNode* const newconstp = varValuep->cloneTree(false);
|
||||
refp->replaceWith(newconstp);
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool cantUnroll(AstNode* nodep, const char* reason) const {
|
||||
UINFO(4, " Can't Unroll: " << reason << " :" << nodep);
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool forUnrollCheck(
|
||||
AstGenFor* const nodep,
|
||||
AstNode* const initp, // Maybe under nodep (no nextp), or standalone (ignore nextp)
|
||||
AstNodeExpr* condp,
|
||||
AstNode* const incp, // Maybe under nodep or in bodysp
|
||||
AstNode* bodysp) {
|
||||
// To keep the IF levels low, we return as each test fails.
|
||||
UINFO(4, " FOR Check " << nodep);
|
||||
if (initp) UINFO(6, " Init " << initp);
|
||||
if (condp) UINFO(6, " Cond " << condp);
|
||||
if (incp) UINFO(6, " Inc " << incp);
|
||||
|
||||
// Initial value check
|
||||
AstAssign* const initAssp = VN_CAST(initp, Assign);
|
||||
if (!initAssp) return cantUnroll(nodep, "no initial assignment");
|
||||
UASSERT_OBJ(!(initp->nextp() && initp->nextp() != nodep), nodep,
|
||||
"initial assignment shouldn't be a list");
|
||||
if (!VN_IS(initAssp->lhsp(), VarRef)) {
|
||||
return cantUnroll(nodep, "no initial assignment to simple variable");
|
||||
}
|
||||
//
|
||||
// Condition check
|
||||
UASSERT_OBJ(!condp->nextp(), nodep, "conditional shouldn't be a list");
|
||||
//
|
||||
// Assignment of next value check
|
||||
const AstAssign* const incAssp = VN_CAST(incp, Assign);
|
||||
if (!incAssp) return cantUnroll(nodep, "no increment assignment");
|
||||
if (incAssp->nextp()) return cantUnroll(nodep, "multiple increments");
|
||||
|
||||
m_forVarp = VN_AS(initAssp->lhsp(), VarRef)->varp();
|
||||
if (!m_forVarp->isGenVar()) {
|
||||
nodep->v3error("Non-genvar used in generate for: " << m_forVarp->prettyNameQ());
|
||||
}
|
||||
V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change
|
||||
|
||||
// This check shouldn't be needed when using V3Simulate
|
||||
// however, for repeat loops, the loop variable is auto-generated
|
||||
// and the initp statements will reference a variable outside of the initp scope
|
||||
// alas, failing to simulate.
|
||||
const AstConst* const constInitp = VN_CAST(initAssp->rhsp(), Const);
|
||||
if (!constInitp) return cantUnroll(nodep, "non-constant initializer");
|
||||
|
||||
//
|
||||
// Now, make sure there's no assignment to this variable in the loop
|
||||
m_varModeCheck = true;
|
||||
m_varAssignHit = false;
|
||||
m_forkHit = false;
|
||||
m_ignoreIncp = incp;
|
||||
iterateAndNextNull(bodysp);
|
||||
iterateAndNextNull(incp);
|
||||
m_varModeCheck = false;
|
||||
m_ignoreIncp = nullptr;
|
||||
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
||||
|
||||
if (m_forkHit) return cantUnroll(nodep, "fork inside loop");
|
||||
|
||||
//
|
||||
UINFO(8, " Loop Variable: " << m_forVarp);
|
||||
UINFOTREE(9, nodep, "", "for");
|
||||
|
||||
// Finally, we can do it
|
||||
if (!forUnroller(nodep, initAssp, condp, incp, bodysp)) {
|
||||
return cantUnroll(nodep, "Unable to unroll loop");
|
||||
}
|
||||
VL_DANGLING(nodep);
|
||||
// Cleanup
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simulateTree(AstNodeExpr* nodep, const V3Number* loopValue, AstNode* dtypep,
|
||||
V3Number& outNum) {
|
||||
AstNode* clonep = nodep->cloneTree(true);
|
||||
UASSERT_OBJ(clonep, nodep, "Failed to clone tree");
|
||||
if (loopValue) {
|
||||
AstConst* varValuep = new AstConst{nodep->fileline(), *loopValue};
|
||||
// Iteration requires a back, so put under temporary node
|
||||
AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep, false};
|
||||
replaceVarRef(tempp->stmtsp(), varValuep);
|
||||
clonep = tempp->stmtsp()->unlinkFrBackWithNext();
|
||||
VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr);
|
||||
VL_DO_DANGLING(pushDeletep(varValuep), varValuep);
|
||||
}
|
||||
SimulateVisitor simvis;
|
||||
simvis.mainParamEmulate(clonep);
|
||||
if (!simvis.optimizable()) {
|
||||
UINFO(4, "Unable to simulate");
|
||||
UINFOTREE(9, nodep, "", "_simtree");
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return false;
|
||||
}
|
||||
// Fetch the result
|
||||
V3Number* resp = simvis.fetchNumberNull(clonep);
|
||||
if (!resp) {
|
||||
UINFO(3, "No number returned from simulation");
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return false;
|
||||
}
|
||||
// Patch up datatype
|
||||
if (dtypep) {
|
||||
AstConst new_con{clonep->fileline(), *resp};
|
||||
new_con.dtypeFrom(dtypep);
|
||||
outNum = new_con.num();
|
||||
outNum.isSigned(dtypep->isSigned());
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return true;
|
||||
}
|
||||
outNum = *resp;
|
||||
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool forUnroller(AstGenFor* nodep, AstAssign* initp, AstNodeExpr* condp, AstNode* incp,
|
||||
AstNode* bodysp) {
|
||||
UINFO(9, "forUnroller " << nodep);
|
||||
V3Number loopValue{nodep};
|
||||
if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { //
|
||||
return false;
|
||||
}
|
||||
AstNode* stmtsp = nullptr;
|
||||
if (initp) {
|
||||
initp->unlinkFrBack(); // Always a single statement; nextp() may be nodep
|
||||
// Don't add to list, we do it once, and setting loop index isn't
|
||||
// needed if we have > 1 loop, as we're constant propagating it
|
||||
pushDeletep(initp); // Always cloned below.
|
||||
}
|
||||
if (bodysp) {
|
||||
bodysp->unlinkFrBackWithNext();
|
||||
stmtsp = AstNode::addNext(stmtsp, bodysp); // Maybe null if no body
|
||||
}
|
||||
// Mark variable to disable some later warnings
|
||||
m_forVarp->usedLoopIdx(true);
|
||||
|
||||
AstNode* newbodysp = nullptr;
|
||||
if (stmtsp) {
|
||||
pushDeletep(stmtsp); // Always cloned below.
|
||||
int times = 0;
|
||||
while (true) {
|
||||
UINFO(8, " Looping " << loopValue);
|
||||
V3Number res{nodep};
|
||||
if (!simulateTree(condp, &loopValue, nullptr, res)) {
|
||||
nodep->v3error("Loop unrolling failed.");
|
||||
return false;
|
||||
}
|
||||
if (!res.isEqOne()) {
|
||||
break; // Done with the loop
|
||||
} else {
|
||||
// Replace iterator values with constant
|
||||
AstNode* oneloopp = stmtsp->cloneTree(true);
|
||||
AstConst* varValuep = new AstConst{nodep->fileline(), loopValue};
|
||||
if (oneloopp) {
|
||||
// Iteration requires a back, so put under temporary node
|
||||
AstBegin* const tempp
|
||||
= new AstBegin{oneloopp->fileline(), "[EditWrapper]", oneloopp, false};
|
||||
replaceVarRef(tempp->stmtsp(), varValuep);
|
||||
oneloopp = tempp->stmtsp()->unlinkFrBackWithNext();
|
||||
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
||||
}
|
||||
const string index = AstNode::encodeNumber(varValuep->toSInt());
|
||||
const string nname = m_beginName + "__BRA__" + index + "__KET__";
|
||||
oneloopp = new AstGenBlock{oneloopp->fileline(), nname, oneloopp, false};
|
||||
VL_DO_DANGLING(pushDeletep(varValuep), varValuep);
|
||||
if (newbodysp) {
|
||||
newbodysp->addNext(oneloopp);
|
||||
} else {
|
||||
newbodysp = oneloopp;
|
||||
}
|
||||
|
||||
const int limit = v3Global.opt.unrollCountAdjusted(VOptionBool{}, true, false);
|
||||
if (++times / 3 > limit) {
|
||||
nodep->v3error(
|
||||
"Loop unrolling took too long;"
|
||||
" probably this is an infinite loop, "
|
||||
" or use /*verilator unroll_full*/, or set --unroll-count above "
|
||||
<< times);
|
||||
break;
|
||||
}
|
||||
|
||||
// loopValue += valInc
|
||||
AstAssign* const incpass = VN_AS(incp, Assign);
|
||||
V3Number newLoopValue{nodep};
|
||||
if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) {
|
||||
nodep->v3error("Loop unrolling failed");
|
||||
return false;
|
||||
}
|
||||
loopValue.opAssign(newLoopValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!newbodysp) { // initp might have effects after the loop
|
||||
if (initp) { // GENFOR(ASSIGN(...)) need to move under a new Initial
|
||||
newbodysp = new AstInitial{initp->fileline(), initp->cloneTree(true)};
|
||||
} else {
|
||||
newbodysp = initp ? initp->cloneTree(true) : nullptr;
|
||||
}
|
||||
}
|
||||
// Replace the FOR()
|
||||
if (newbodysp) {
|
||||
nodep->replaceWith(newbodysp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
if (newbodysp) UINFOTREE(9, newbodysp, "", "_new");
|
||||
return true;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (m_varModeCheck && nodep->varp() == m_forVarp && nodep->access().isWriteOrRW()) {
|
||||
UINFO(8, " Itervar assigned to: " << nodep);
|
||||
m_varAssignHit = true;
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
if (m_varModeCheck && nodep == m_ignoreIncp) {
|
||||
// Ignore subtree that is the increment
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
UnrollGenVisitor() = default;
|
||||
|
||||
void unroll(AstGenFor* nodep, const std::string& beginName) {
|
||||
m_forVarp = nullptr; // Iterator variable
|
||||
m_ignoreIncp = nullptr; // Increment node to ignore
|
||||
m_varModeCheck = false; // Just checking RHS assignments
|
||||
m_varAssignHit = false; // Assign var hit
|
||||
m_forkHit = false; // Fork hit
|
||||
m_beginName = beginName;
|
||||
|
||||
// Constify before unroll call, as it may change what is underneath.
|
||||
if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change
|
||||
if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change
|
||||
if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); // incsp may change
|
||||
if (nodep->condp()->isZero()) {
|
||||
// We don't need to do any loops. Remove the GenFor,
|
||||
// Genvar's don't care about any initial assignments.
|
||||
//
|
||||
// Note normal For's can't do exactly this deletion, as
|
||||
// we'd need to initialize the variable to the initial
|
||||
// condition, but they'll become while's which can be
|
||||
// deleted by V3Const.
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
} else if (forUnrollCheck(nodep, nodep->initsp(), nodep->condp(), nodep->incsp(),
|
||||
nodep->itemsp())) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement
|
||||
} else {
|
||||
nodep->v3error("For loop doesn't have genvar index, or is malformed");
|
||||
// We will die, do it gracefully
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// GenForUnroller members
|
||||
|
||||
GenForUnroller::GenForUnroller()
|
||||
: m_unrollerp{new UnrollGenVisitor{}} {}
|
||||
GenForUnroller::~GenForUnroller() { VL_DO_DANGLING(delete m_unrollerp, m_unrollerp); }
|
||||
void GenForUnroller::unroll(AstGenFor* nodep, const std::string& beginName) {
|
||||
m_unrollerp->unroll(nodep, beginName);
|
||||
}
|
||||
|
|
@ -5466,18 +5466,20 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void visit(AstLoop* nodep) override {
|
||||
UASSERT_OBJ(!nodep->contsp(), nodep, "'contsp' only used before LinkJump");
|
||||
assertAtStatement(nodep);
|
||||
userIterateAndNext(nodep->stmtsp(), nullptr);
|
||||
}
|
||||
void visit(AstLoopTest* nodep) override {
|
||||
assertAtStatement(nodep);
|
||||
iterateCheckBool(nodep, "Loop Condition", nodep->condp(), BOTH);
|
||||
}
|
||||
void visit(AstRepeat* nodep) override {
|
||||
assertAtStatement(nodep);
|
||||
userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
|
||||
userIterateAndNext(nodep->stmtsp(), nullptr);
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
assertAtStatement(nodep);
|
||||
iterateCheckBool(nodep, "For Test Condition", nodep->condp(),
|
||||
BOTH); // it's like an if() condition.
|
||||
userIterateAndNext(nodep->stmtsp(), nullptr);
|
||||
userIterateAndNext(nodep->incsp(), nullptr);
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
assertAtStatement(nodep);
|
||||
// UINFOTREE(1, nodep, "", "IfPre");
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ static void process() {
|
|||
// After V3Task so task internal variables will get renamed
|
||||
V3Name::nameAll(v3Global.rootp());
|
||||
|
||||
// Loop unrolling & convert FORs to WHILEs
|
||||
// Loop unrolling
|
||||
V3Unroll::unrollAll(v3Global.rootp());
|
||||
|
||||
// Expand slices of arrays
|
||||
|
|
|
|||
|
|
@ -3590,14 +3590,31 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
| yP_MINUSGTGT delay_or_event_controlE expr ';'
|
||||
{ $$ = new AstFireEvent{$1, $3, true}; }
|
||||
//
|
||||
// // IEEE: loop_statement
|
||||
// do/for/forever/while loops all modelled as AstLoop
|
||||
| yDO stmtBlock yWHILE '(' expr ')' ';'
|
||||
{ AstLoop* const loopp = new AstLoop{$1};
|
||||
loopp->addStmtsp($2);
|
||||
loopp->addContsp(new AstLoopTest{$<fl>5, loopp, $5});
|
||||
$$ = loopp; }
|
||||
| yFOR '(' { VARRESET_NONLIST(UNKNOWN); } for_initializationE ';' exprE ';' for_stepE ')' stmtBlock
|
||||
{ AstBegin* const blockp = new AstBegin{$1, "", $4, true};
|
||||
AstLoop* const loopp = new AstLoop{$1};
|
||||
if ($6) loopp->addStmtsp(new AstLoopTest{$<fl>6, loopp, $6});
|
||||
loopp->addStmtsp($10);
|
||||
loopp->addContsp($8);
|
||||
blockp->addStmtsp(loopp);
|
||||
$$ = blockp; }
|
||||
| yFOREVER stmtBlock
|
||||
{ $$ = new AstWhile{$1, new AstConst{$1, AstConst::BitTrue{}}, $2}; }
|
||||
{ AstLoop* const loopp = new AstLoop{$1};
|
||||
loopp->addStmtsp($2);
|
||||
$$ = loopp; }
|
||||
| yWHILE '(' expr ')' stmtBlock
|
||||
{ AstLoop* const loopp = new AstLoop{$1};
|
||||
loopp->addStmtsp(new AstLoopTest{$<fl>3, loopp, $3});
|
||||
loopp->addStmtsp($5);
|
||||
$$ = loopp; }
|
||||
// Other loop statements
|
||||
| yREPEAT '(' expr ')' stmtBlock { $$ = new AstRepeat{$1, $3, $5}; }
|
||||
| yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile{$1, $3, $5}; }
|
||||
// // for's first ';' is in for_initialization
|
||||
| statementFor { $$ = $1; }
|
||||
| yDO stmtBlock yWHILE '(' expr ')' ';' { $$ = new AstDoWhile{$1, $5, $2}; }
|
||||
// // IEEE says array_identifier here, but dotted accepted in VMM and 1800-2009
|
||||
| yFOREACH '(' idClassSelForeach ')' stmtBlock
|
||||
{ $$ = new AstBegin{$1, "", new AstForeach{$1, $3, $5}, true}; }
|
||||
|
|
@ -3660,18 +3677,6 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
{ $$ = nullptr; BBUNSUP($1, "Unsupported: expect"); DEL($3, $6); }
|
||||
;
|
||||
|
||||
statementFor<beginp>: // IEEE: part of statement
|
||||
yFOR beginForParen for_initialization expr ';' for_stepE ')' stmtBlock
|
||||
{ $$ = new AstBegin{$1, "", $3, true};
|
||||
$$->addStmtsp(new AstWhile{$1, $4, $8, $6}); }
|
||||
| yFOR beginForParen for_initialization ';' for_stepE ')' stmtBlock
|
||||
{ $$ = new AstBegin{$1, "", $3, true};
|
||||
$$->addStmtsp(new AstWhile{$1, new AstConst{$1, AstConst::BitTrue{}}, $7, $5}); }
|
||||
;
|
||||
beginForParen: // IEEE: Part of statement (for loop beginning paren)
|
||||
'(' { VARRESET_NONLIST(UNKNOWN); }
|
||||
;
|
||||
|
||||
statementVerilatorPragmas<nodep>:
|
||||
yVL_COVERAGE_BLOCK_OFF
|
||||
{ $$ = new AstPragma{$1, VPragmaType::COVERAGE_BLOCK_OFF}; }
|
||||
|
|
@ -3948,11 +3953,9 @@ assignment_pattern<patternp>: // ==IEEE: assignment_pattern
|
|||
;
|
||||
|
||||
// "datatype id = x {, id = x }" | "yaId = x {, id=x}" is legal
|
||||
for_initialization<nodep>: // ==IEEE: for_initialization + for_variable_declaration + extra terminating ";"
|
||||
// // IEEE: for_variable_declaration
|
||||
for_initializationItemList ';' { $$ = $1; }
|
||||
// // IEEE: 1800-2017 empty initialization
|
||||
| ';' { $$ = nullptr; }
|
||||
for_initializationE<nodep>: // ==IEEE: for_initialization + for_variable_declaration
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| for_initializationItemList { $$ = $1; }
|
||||
;
|
||||
|
||||
for_initializationItemList<nodep>: // IEEE: [for_variable_declaration...]
|
||||
|
|
|
|||
|
|
@ -148,12 +148,12 @@
|
|||
-000000 point: comment=block hier=top.t
|
||||
~000010 do begin
|
||||
-000000 point: comment=block hier=top.t
|
||||
+000010 point: comment=if hier=top.t
|
||||
~000010 $write("");
|
||||
-000000 point: comment=block hier=top.t
|
||||
+000010 point: comment=if hier=top.t
|
||||
%000000 end while (0);
|
||||
+000010 point: comment=block hier=top.t
|
||||
000010 $write("");
|
||||
+000010 point: comment=block hier=top.t
|
||||
~000010 end while (0);
|
||||
-000000 point: comment=block hier=top.t
|
||||
+000010 point: comment=block hier=top.t
|
||||
//===
|
||||
// Task and complicated
|
||||
%000001 if (cyc==3) begin
|
||||
|
|
@ -522,6 +522,8 @@
|
|||
end
|
||||
000044 for (int i = 0; i < 7; i = (i > 4) ? i + 1 : i + 2) begin
|
||||
+000011 point: comment=block hier=top.t.cond1
|
||||
+000011 point: comment=cond_then hier=top.t.cond1
|
||||
+000033 point: comment=cond_else hier=top.t.cond1
|
||||
+000044 point: comment=block hier=top.t.cond1
|
||||
000044 k = 1'(i);
|
||||
+000044 point: comment=block hier=top.t.cond1
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ DA:105,10
|
|||
BRDA:105,0,0,0
|
||||
BRDA:105,0,1,10
|
||||
DA:106,10
|
||||
BRDA:106,0,0,0
|
||||
BRDA:106,0,1,10
|
||||
DA:107,0
|
||||
DA:107,10
|
||||
BRDA:107,0,0,0
|
||||
BRDA:107,0,1,10
|
||||
DA:110,1
|
||||
DA:111,1
|
||||
DA:113,1
|
||||
|
|
@ -200,12 +200,14 @@ BRDA:354,0,1,55
|
|||
DA:355,55
|
||||
DA:357,44
|
||||
BRDA:357,0,0,11
|
||||
BRDA:357,0,1,44
|
||||
BRDA:357,0,1,11
|
||||
BRDA:357,0,2,33
|
||||
BRDA:357,0,3,44
|
||||
DA:358,44
|
||||
DA:361,11
|
||||
BRDA:361,0,0,0
|
||||
BRDA:361,0,1,11
|
||||
DA:362,11
|
||||
BRF:77
|
||||
BRH:30
|
||||
BRF:79
|
||||
BRH:32
|
||||
end_of_record
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -20,7 +20,7 @@
|
|||
| ^~~~~~~~~~~~~~
|
||||
%Error: t/t_func_const_bad.v:36:20: Expecting expression to be constant, but can't determine constant for FUNCREF 'f_bad_infinite'
|
||||
: ... note: In instance 't'
|
||||
t/t_func_const_bad.v:38:7: ... Location of non-constant WHILE: Loop unrolling took too long; probably this is aninfinite loop, or use /*verilator unroll_full*/, or set --unroll-count above 16386
|
||||
t/t_func_const_bad.v:38:7: ... Location of non-constant LOOP: Loop unrolling took too long; probably this is aninfinite loop, or use /*verilator unroll_full*/, or set --unroll-count above 16386
|
||||
t/t_func_const_bad.v:36:20: ... Called from 'f_bad_infinite()' with parameters:
|
||||
a = ?32?h3
|
||||
36 | localparam B4 = f_bad_infinite(3);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@
|
|||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:23:25: Unsupported: write to virtual interface in loop condition
|
||||
23 | while (write_data(vif.data));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:24:34: Unsupported: write to virtual interface in loop condition
|
||||
24 | for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data)));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:24:66: Unsupported: write to virtual interface in loop increment statement
|
||||
24 | for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data)));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:24:30: Unsupported: write to virtual interface in loop condition
|
||||
24 | do ; while (write_data(vif.data));
|
||||
| ^~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module t;
|
|||
initial begin
|
||||
if (write_data(vif.data)) $write("dummy op");
|
||||
while (write_data(vif.data));
|
||||
for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data)));
|
||||
do ; while (write_data(vif.data));
|
||||
for (int i = 0; write_data(vif.data++); i++);
|
||||
end
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -85,199 +85,197 @@
|
|||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(OC)","loc":"d,18:10,18:11","dtypep":"(SB)","access":"WR","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []},
|
||||
{"type":"WHILE","name":"","addr":"(PC)","loc":"d,18:5,18:8",
|
||||
"condp": [
|
||||
{"type":"GTS","name":"","addr":"(QC)","loc":"d,18:18,18:19","dtypep":"(RC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"32'sh7","addr":"(SC)","loc":"d,18:20,18:21","dtypep":"(NC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(TC)","loc":"d,18:16,18:17","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
],
|
||||
{"type":"LOOP","name":"","addr":"(PC)","loc":"d,18:5,18:8","unroll":"default",
|
||||
"stmtsp": [
|
||||
{"type":"ASSIGN","name":"","addr":"(UC)","loc":"d,19:14,19:15","dtypep":"(RC)",
|
||||
"rhsp": [
|
||||
{"type":"EQ","name":"","addr":"(VC)","loc":"d,19:31,19:33","dtypep":"(RC)",
|
||||
{"type":"LOOPTEST","name":"","addr":"(QC)","loc":"d,18:16,18:17",
|
||||
"condp": [
|
||||
{"type":"GTS","name":"","addr":"(RC)","loc":"d,18:18,18:19","dtypep":"(SC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"2'h0","addr":"(WC)","loc":"d,19:34,19:39","dtypep":"(XC)"}
|
||||
{"type":"CONST","name":"32'sh7","addr":"(TC)","loc":"d,18:20,18:21","dtypep":"(NC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"SEL","name":"","addr":"(YC)","loc":"d,19:20,19:21","dtypep":"(XC)","widthConst":2,"declRange":"[15:0]","declElWidth":1,
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(UC)","loc":"d,18:16,18:17","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
]},
|
||||
{"type":"ASSIGN","name":"","addr":"(VC)","loc":"d,19:14,19:15","dtypep":"(SC)",
|
||||
"rhsp": [
|
||||
{"type":"EQ","name":"","addr":"(WC)","loc":"d,19:31,19:33","dtypep":"(SC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"2'h0","addr":"(XC)","loc":"d,19:34,19:39","dtypep":"(YC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"SEL","name":"","addr":"(ZC)","loc":"d,19:20,19:21","dtypep":"(YC)","widthConst":2,"declRange":"[15:0]","declElWidth":1,
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__val","addr":"(ZC)","loc":"d,19:17,19:20","dtypep":"(H)","access":"RD","varp":"(OB)","varScopep":"(NB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__val","addr":"(AD)","loc":"d,19:17,19:20","dtypep":"(H)","access":"RD","varp":"(OB)","varScopep":"(NB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"SEL","name":"","addr":"(AD)","loc":"d,19:22,19:23","dtypep":"(BD)","widthConst":4,
|
||||
{"type":"SEL","name":"","addr":"(BD)","loc":"d,19:22,19:23","dtypep":"(CD)","widthConst":4,
|
||||
"fromp": [
|
||||
{"type":"MULS","name":"","addr":"(CD)","loc":"d,19:22,19:23","dtypep":"(NC)",
|
||||
{"type":"MULS","name":"","addr":"(DD)","loc":"d,19:22,19:23","dtypep":"(NC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"32'sh2","addr":"(DD)","loc":"d,19:23,19:24","dtypep":"(NC)"}
|
||||
{"type":"CONST","name":"32'sh2","addr":"(ED)","loc":"d,19:23,19:24","dtypep":"(NC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(ED)","loc":"d,19:21,19:22","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(FD)","loc":"d,19:21,19:22","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"CONST","name":"32'h0","addr":"(FD)","loc":"d,19:22,19:23","dtypep":"(GD)"}
|
||||
{"type":"CONST","name":"32'h0","addr":"(GD)","loc":"d,19:22,19:23","dtypep":"(HD)"}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"SEL","name":"","addr":"(HD)","loc":"d,19:10,19:11","dtypep":"(RC)","widthConst":1,"declRange":"[6:0]","declElWidth":1,
|
||||
{"type":"SEL","name":"","addr":"(ID)","loc":"d,19:10,19:11","dtypep":"(SC)","widthConst":1,"declRange":"[6:0]","declElWidth":1,
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__ret","addr":"(ID)","loc":"d,19:7,19:10","dtypep":"(K)","access":"WR","varp":"(QB)","varScopep":"(PB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__ret","addr":"(JD)","loc":"d,19:7,19:10","dtypep":"(K)","access":"WR","varp":"(QB)","varScopep":"(PB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"SEL","name":"","addr":"(JD)","loc":"d,19:11,19:12","dtypep":"(KD)","widthConst":3,
|
||||
{"type":"SEL","name":"","addr":"(KD)","loc":"d,19:11,19:12","dtypep":"(LD)","widthConst":3,
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(LD)","loc":"d,19:11,19:12","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(MD)","loc":"d,19:11,19:12","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"CONST","name":"32'h0","addr":"(MD)","loc":"d,19:11,19:12","dtypep":"(GD)"}
|
||||
{"type":"CONST","name":"32'h0","addr":"(ND)","loc":"d,19:11,19:12","dtypep":"(HD)"}
|
||||
]}
|
||||
]}
|
||||
],"timingControlp": []}
|
||||
],
|
||||
"incsp": [
|
||||
{"type":"ASSIGN","name":"","addr":"(ND)","loc":"d,18:24,18:26","dtypep":"(SB)",
|
||||
],"timingControlp": []},
|
||||
{"type":"ASSIGN","name":"","addr":"(OD)","loc":"d,18:24,18:26","dtypep":"(SB)",
|
||||
"rhsp": [
|
||||
{"type":"ADD","name":"","addr":"(OD)","loc":"d,18:24,18:26","dtypep":"(GD)",
|
||||
{"type":"ADD","name":"","addr":"(PD)","loc":"d,18:24,18:26","dtypep":"(HD)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"32'h1","addr":"(PD)","loc":"d,18:24,18:26","dtypep":"(GD)"}
|
||||
{"type":"CONST","name":"32'h1","addr":"(QD)","loc":"d,18:24,18:26","dtypep":"(HD)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(QD)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(RD)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"RD","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(RD)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"WR","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__i","addr":"(SD)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"WR","varp":"(TB)","varScopep":"(RB)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []}
|
||||
]},
|
||||
{"type":"ASSIGN","name":"","addr":"(SD)","loc":"d,21:5,21:11","dtypep":"(K)",
|
||||
],"contsp": []},
|
||||
{"type":"ASSIGN","name":"","addr":"(TD)","loc":"d,21:5,21:11","dtypep":"(K)",
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__ret","addr":"(TD)","loc":"d,21:12,21:15","dtypep":"(K)","access":"RD","varp":"(QB)","varScopep":"(PB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__ret","addr":"(UD)","loc":"d,21:12,21:15","dtypep":"(K)","access":"RD","varp":"(QB)","varScopep":"(PB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__Vfuncout","addr":"(UD)","loc":"d,21:5,21:11","dtypep":"(K)","access":"WR","varp":"(MB)","varScopep":"(LB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__Vfuncout","addr":"(VD)","loc":"d,21:5,21:11","dtypep":"(K)","access":"WR","varp":"(MB)","varScopep":"(LB)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []},
|
||||
{"type":"ASSIGN","name":"","addr":"(VD)","loc":"d,24:14,24:15","dtypep":"(K)",
|
||||
{"type":"ASSIGN","name":"","addr":"(WD)","loc":"d,24:14,24:15","dtypep":"(K)",
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__Vfuncout","addr":"(WD)","loc":"d,24:16,24:19","dtypep":"(K)","access":"RD","varp":"(MB)","varScopep":"(LB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__0__Vfuncout","addr":"(XD)","loc":"d,24:16,24:19","dtypep":"(K)","access":"RD","varp":"(MB)","varScopep":"(LB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"o_a","addr":"(XD)","loc":"d,24:10,24:13","dtypep":"(K)","access":"WR","varp":"(J)","varScopep":"(T)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"o_a","addr":"(YD)","loc":"d,24:10,24:13","dtypep":"(K)","access":"WR","varp":"(J)","varScopep":"(T)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []}
|
||||
]},
|
||||
{"type":"ALWAYS","name":"","addr":"(YD)","loc":"d,25:14,25:15","keyword":"always","isSuspendable":false,"needProcess":false,"sentreep": [],
|
||||
{"type":"ALWAYS","name":"","addr":"(ZD)","loc":"d,25:14,25:15","keyword":"always","isSuspendable":false,"needProcess":false,"sentreep": [],
|
||||
"stmtsp": [
|
||||
{"type":"COMMENT","name":"Function: foo","addr":"(ZD)","loc":"d,25:16,25:19"},
|
||||
{"type":"ASSIGN","name":"","addr":"(AE)","loc":"d,25:20,25:23","dtypep":"(H)",
|
||||
{"type":"COMMENT","name":"Function: foo","addr":"(AE)","loc":"d,25:16,25:19"},
|
||||
{"type":"ASSIGN","name":"","addr":"(BE)","loc":"d,25:20,25:23","dtypep":"(H)",
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"i_b","addr":"(BE)","loc":"d,25:20,25:23","dtypep":"(H)","access":"RD","varp":"(I)","varScopep":"(S)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"i_b","addr":"(CE)","loc":"d,25:20,25:23","dtypep":"(H)","access":"RD","varp":"(I)","varScopep":"(S)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__val","addr":"(CE)","loc":"d,15:57,15:60","dtypep":"(H)","access":"WR","varp":"(XB)","varScopep":"(WB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__val","addr":"(DE)","loc":"d,15:57,15:60","dtypep":"(H)","access":"WR","varp":"(XB)","varScopep":"(WB)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []},
|
||||
{"type":"CRESET","name":"","addr":"(DE)","loc":"d,16:17,16:20","constructing":false,
|
||||
{"type":"CRESET","name":"","addr":"(EE)","loc":"d,16:17,16:20","constructing":false,
|
||||
"varrefp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__ret","addr":"(EE)","loc":"d,16:17,16:20","dtypep":"(K)","access":"WR","varp":"(ZB)","varScopep":"(YB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__ret","addr":"(FE)","loc":"d,16:17,16:20","dtypep":"(K)","access":"WR","varp":"(ZB)","varScopep":"(YB)","classOrPackagep":"UNLINKED"}
|
||||
]},
|
||||
{"type":"CRESET","name":"","addr":"(FE)","loc":"d,17:13,17:14","constructing":false,
|
||||
{"type":"CRESET","name":"","addr":"(GE)","loc":"d,17:13,17:14","constructing":false,
|
||||
"varrefp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(GE)","loc":"d,17:13,17:14","dtypep":"(SB)","access":"WR","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(HE)","loc":"d,17:13,17:14","dtypep":"(SB)","access":"WR","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
]},
|
||||
{"type":"ASSIGN","name":"","addr":"(HE)","loc":"d,18:11,18:12","dtypep":"(SB)",
|
||||
{"type":"ASSIGN","name":"","addr":"(IE)","loc":"d,18:11,18:12","dtypep":"(SB)",
|
||||
"rhsp": [
|
||||
{"type":"CONST","name":"32'sh0","addr":"(IE)","loc":"d,18:12,18:13","dtypep":"(NC)"}
|
||||
{"type":"CONST","name":"32'sh0","addr":"(JE)","loc":"d,18:12,18:13","dtypep":"(NC)"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(JE)","loc":"d,18:10,18:11","dtypep":"(SB)","access":"WR","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(KE)","loc":"d,18:10,18:11","dtypep":"(SB)","access":"WR","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []},
|
||||
{"type":"WHILE","name":"","addr":"(KE)","loc":"d,18:5,18:8",
|
||||
"condp": [
|
||||
{"type":"GTS","name":"","addr":"(LE)","loc":"d,18:18,18:19","dtypep":"(RC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"32'sh7","addr":"(ME)","loc":"d,18:20,18:21","dtypep":"(NC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(NE)","loc":"d,18:16,18:17","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
],
|
||||
{"type":"LOOP","name":"","addr":"(LE)","loc":"d,18:5,18:8","unroll":"default",
|
||||
"stmtsp": [
|
||||
{"type":"ASSIGN","name":"","addr":"(OE)","loc":"d,19:14,19:15","dtypep":"(RC)",
|
||||
"rhsp": [
|
||||
{"type":"EQ","name":"","addr":"(PE)","loc":"d,19:31,19:33","dtypep":"(RC)",
|
||||
{"type":"LOOPTEST","name":"","addr":"(ME)","loc":"d,18:16,18:17",
|
||||
"condp": [
|
||||
{"type":"GTS","name":"","addr":"(NE)","loc":"d,18:18,18:19","dtypep":"(SC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"2'h0","addr":"(QE)","loc":"d,19:34,19:39","dtypep":"(XC)"}
|
||||
{"type":"CONST","name":"32'sh7","addr":"(OE)","loc":"d,18:20,18:21","dtypep":"(NC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"SEL","name":"","addr":"(RE)","loc":"d,19:20,19:21","dtypep":"(XC)","widthConst":2,"declRange":"[15:0]","declElWidth":1,
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(PE)","loc":"d,18:16,18:17","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
]},
|
||||
{"type":"ASSIGN","name":"","addr":"(QE)","loc":"d,19:14,19:15","dtypep":"(SC)",
|
||||
"rhsp": [
|
||||
{"type":"EQ","name":"","addr":"(RE)","loc":"d,19:31,19:33","dtypep":"(SC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"2'h0","addr":"(SE)","loc":"d,19:34,19:39","dtypep":"(YC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"SEL","name":"","addr":"(TE)","loc":"d,19:20,19:21","dtypep":"(YC)","widthConst":2,"declRange":"[15:0]","declElWidth":1,
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__val","addr":"(SE)","loc":"d,19:17,19:20","dtypep":"(H)","access":"RD","varp":"(XB)","varScopep":"(WB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__val","addr":"(UE)","loc":"d,19:17,19:20","dtypep":"(H)","access":"RD","varp":"(XB)","varScopep":"(WB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"SEL","name":"","addr":"(TE)","loc":"d,19:22,19:23","dtypep":"(BD)","widthConst":4,
|
||||
{"type":"SEL","name":"","addr":"(VE)","loc":"d,19:22,19:23","dtypep":"(CD)","widthConst":4,
|
||||
"fromp": [
|
||||
{"type":"MULS","name":"","addr":"(UE)","loc":"d,19:22,19:23","dtypep":"(NC)",
|
||||
{"type":"MULS","name":"","addr":"(WE)","loc":"d,19:22,19:23","dtypep":"(NC)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"32'sh2","addr":"(VE)","loc":"d,19:23,19:24","dtypep":"(NC)"}
|
||||
{"type":"CONST","name":"32'sh2","addr":"(XE)","loc":"d,19:23,19:24","dtypep":"(NC)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(WE)","loc":"d,19:21,19:22","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(YE)","loc":"d,19:21,19:22","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"CONST","name":"32'h0","addr":"(XE)","loc":"d,19:22,19:23","dtypep":"(GD)"}
|
||||
{"type":"CONST","name":"32'h0","addr":"(ZE)","loc":"d,19:22,19:23","dtypep":"(HD)"}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"SEL","name":"","addr":"(YE)","loc":"d,19:10,19:11","dtypep":"(RC)","widthConst":1,"declRange":"[6:0]","declElWidth":1,
|
||||
{"type":"SEL","name":"","addr":"(AF)","loc":"d,19:10,19:11","dtypep":"(SC)","widthConst":1,"declRange":"[6:0]","declElWidth":1,
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__ret","addr":"(ZE)","loc":"d,19:7,19:10","dtypep":"(K)","access":"WR","varp":"(ZB)","varScopep":"(YB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__ret","addr":"(BF)","loc":"d,19:7,19:10","dtypep":"(K)","access":"WR","varp":"(ZB)","varScopep":"(YB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"SEL","name":"","addr":"(AF)","loc":"d,19:11,19:12","dtypep":"(KD)","widthConst":3,
|
||||
{"type":"SEL","name":"","addr":"(CF)","loc":"d,19:11,19:12","dtypep":"(LD)","widthConst":3,
|
||||
"fromp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(BF)","loc":"d,19:11,19:12","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(DF)","loc":"d,19:11,19:12","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lsbp": [
|
||||
{"type":"CONST","name":"32'h0","addr":"(CF)","loc":"d,19:11,19:12","dtypep":"(GD)"}
|
||||
{"type":"CONST","name":"32'h0","addr":"(EF)","loc":"d,19:11,19:12","dtypep":"(HD)"}
|
||||
]}
|
||||
]}
|
||||
],"timingControlp": []}
|
||||
],
|
||||
"incsp": [
|
||||
{"type":"ASSIGN","name":"","addr":"(DF)","loc":"d,18:24,18:26","dtypep":"(SB)",
|
||||
],"timingControlp": []},
|
||||
{"type":"ASSIGN","name":"","addr":"(FF)","loc":"d,18:24,18:26","dtypep":"(SB)",
|
||||
"rhsp": [
|
||||
{"type":"ADD","name":"","addr":"(EF)","loc":"d,18:24,18:26","dtypep":"(GD)",
|
||||
{"type":"ADD","name":"","addr":"(GF)","loc":"d,18:24,18:26","dtypep":"(HD)",
|
||||
"lhsp": [
|
||||
{"type":"CONST","name":"32'h1","addr":"(FF)","loc":"d,18:24,18:26","dtypep":"(GD)"}
|
||||
{"type":"CONST","name":"32'h1","addr":"(HF)","loc":"d,18:24,18:26","dtypep":"(HD)"}
|
||||
],
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(GF)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(IF)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"RD","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
]}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(HF)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"WR","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__i","addr":"(JF)","loc":"d,18:23,18:24","dtypep":"(SB)","access":"WR","varp":"(BC)","varScopep":"(AC)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []}
|
||||
]},
|
||||
{"type":"ASSIGN","name":"","addr":"(IF)","loc":"d,21:5,21:11","dtypep":"(K)",
|
||||
],"contsp": []},
|
||||
{"type":"ASSIGN","name":"","addr":"(KF)","loc":"d,21:5,21:11","dtypep":"(K)",
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__ret","addr":"(JF)","loc":"d,21:12,21:15","dtypep":"(K)","access":"RD","varp":"(ZB)","varScopep":"(YB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__ret","addr":"(LF)","loc":"d,21:12,21:15","dtypep":"(K)","access":"RD","varp":"(ZB)","varScopep":"(YB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__Vfuncout","addr":"(KF)","loc":"d,21:5,21:11","dtypep":"(K)","access":"WR","varp":"(VB)","varScopep":"(UB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__Vfuncout","addr":"(MF)","loc":"d,21:5,21:11","dtypep":"(K)","access":"WR","varp":"(VB)","varScopep":"(UB)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []},
|
||||
{"type":"ASSIGN","name":"","addr":"(LF)","loc":"d,25:14,25:15","dtypep":"(K)",
|
||||
{"type":"ASSIGN","name":"","addr":"(NF)","loc":"d,25:14,25:15","dtypep":"(K)",
|
||||
"rhsp": [
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__Vfuncout","addr":"(MF)","loc":"d,25:16,25:19","dtypep":"(K)","access":"RD","varp":"(VB)","varScopep":"(UB)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"__Vfunc_vlvbound_test.foo__1__Vfuncout","addr":"(OF)","loc":"d,25:16,25:19","dtypep":"(K)","access":"RD","varp":"(VB)","varScopep":"(UB)","classOrPackagep":"UNLINKED"}
|
||||
],
|
||||
"lhsp": [
|
||||
{"type":"VARREF","name":"o_b","addr":"(NF)","loc":"d,25:10,25:13","dtypep":"(K)","access":"WR","varp":"(L)","varScopep":"(U)","classOrPackagep":"UNLINKED"}
|
||||
{"type":"VARREF","name":"o_b","addr":"(PF)","loc":"d,25:10,25:13","dtypep":"(K)","access":"WR","varp":"(L)","varScopep":"(U)","classOrPackagep":"UNLINKED"}
|
||||
],"timingControlp": []}
|
||||
]}
|
||||
],"inlinesp": []}
|
||||
|
|
@ -293,24 +291,24 @@
|
|||
]}
|
||||
],"filesp": [],
|
||||
"miscsp": [
|
||||
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(OF)",
|
||||
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(QF)",
|
||||
"typesp": [
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(RC)","loc":"d,18:18,18:19","dtypep":"(RC)","keyword":"logic","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(XC)","loc":"d,19:34,19:39","dtypep":"(XC)","keyword":"logic","range":"1:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(SC)","loc":"d,18:18,18:19","dtypep":"(SC)","keyword":"logic","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(YC)","loc":"d,19:34,19:39","dtypep":"(YC)","keyword":"logic","range":"1:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(H)","loc":"d,9:11,9:16","dtypep":"(H)","keyword":"logic","range":"15:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(K)","loc":"d,11:12,11:17","dtypep":"(K)","keyword":"logic","range":"6:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"integer","addr":"(SB)","loc":"d,17:5,17:12","dtypep":"(SB)","keyword":"integer","range":"31:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(KD)","loc":"d,19:10,19:11","dtypep":"(KD)","keyword":"logic","range":"2:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(GD)","loc":"d,19:11,19:12","dtypep":"(GD)","keyword":"logic","range":"31:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(BD)","loc":"d,19:20,19:21","dtypep":"(BD)","keyword":"logic","range":"3:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(LD)","loc":"d,19:10,19:11","dtypep":"(LD)","keyword":"logic","range":"2:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(HD)","loc":"d,19:11,19:12","dtypep":"(HD)","keyword":"logic","range":"31:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(CD)","loc":"d,19:20,19:21","dtypep":"(CD)","keyword":"logic","range":"3:0","generic":true,"rangep": []},
|
||||
{"type":"BASICDTYPE","name":"logic","addr":"(NC)","loc":"d,18:12,18:13","dtypep":"(NC)","keyword":"logic","range":"31:0","generic":true,"rangep": []},
|
||||
{"type":"VOIDDTYPE","name":"","addr":"(OF)","loc":"a,0:0,0:0","dtypep":"(OF)","generic":false}
|
||||
{"type":"VOIDDTYPE","name":"","addr":"(QF)","loc":"a,0:0,0:0","dtypep":"(QF)","generic":false}
|
||||
]},
|
||||
{"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0",
|
||||
"modulep": [
|
||||
{"type":"MODULE","name":"@CONST-POOL@","addr":"(PF)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [],
|
||||
{"type":"MODULE","name":"@CONST-POOL@","addr":"(RF)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [],
|
||||
"stmtsp": [
|
||||
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(QF)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(PF)","varsp": [],"blocksp": [],"inlinesp": []}
|
||||
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(SF)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(RF)","varsp": [],"blocksp": [],"inlinesp": []}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,42 @@
|
|||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:172:7: Loop condition is always false; body will never execute
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:169:7: Loop condition is always false
|
||||
: ... note: In instance 't.with_always'
|
||||
172 | while(0);
|
||||
169 | while(0);
|
||||
| ^~~~~
|
||||
... For warning description see https://verilator.org/warn/UNUSEDLOOP?v=latest
|
||||
... Use "/* verilator lint_off UNUSEDLOOP */" and lint_on around source to disable this message.
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:155:7: Loop condition is always false; body will never execute
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:155:7: Loop condition is always false
|
||||
: ... note: In instance 't.non_parametrized_initial'
|
||||
155 | while(0);
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:114:7: Loop condition is always false; body will never execute
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:158:7: Loop condition is always false
|
||||
: ... note: In instance 't.non_parametrized_initial'
|
||||
158 | do ; while(0);
|
||||
| ^~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:114:7: Loop condition is always false
|
||||
114 | while(always_zero < 0) begin
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:156:7: Loop condition is always false; body will never execute
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:156:7: Loop condition is always false
|
||||
156 | while(always_false);
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:157:7: Loop condition is always false; body will never execute
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:157:7: Loop condition is always false
|
||||
157 | while(always_zero < 0);
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:174:7: Loop condition is always false; body will never execute
|
||||
174 | while(always_false) begin
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:171:7: Loop condition is always false
|
||||
171 | while(always_false) begin
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:184:7: Loop condition is always false; body will never execute
|
||||
184 | while(always_zero) begin
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:181:7: Loop condition is always false
|
||||
181 | while(always_zero) begin
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:188:7: Loop condition is always false; body will never execute
|
||||
188 | for (int i = 0; always_zero; i++)
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:185:7: Loop condition is always false
|
||||
185 | for (int i = 0; always_zero; i++)
|
||||
| ^~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:193:7: Loop condition is always false; body will never execute
|
||||
193 | for (int i = 0; i < always_zero; i++)
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:190:7: Loop condition is always false
|
||||
190 | for (int i = 0; i < always_zero; i++)
|
||||
| ^~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:136:7: Loop is not used and will be optimized out
|
||||
136 | while(param_unused_while < always_zero) begin
|
||||
| ^~~~~
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:283:7: Loop is not used and will be optimized out
|
||||
283 | while (m_2_ticked);
|
||||
%Warning-UNUSEDLOOP: t/t_lint_removed_unused_loop_bad.v:280:7: Loop is not used and will be optimized out
|
||||
280 | while (m_2_ticked);
|
||||
| ^~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -155,10 +155,7 @@ module non_parametrized_initial;
|
|||
while(0);
|
||||
while(always_false);
|
||||
while(always_zero < 0);
|
||||
|
||||
// inlined - no warning
|
||||
do begin
|
||||
end while(0);
|
||||
do ; while(0);
|
||||
|
||||
// unrolled - no warning
|
||||
for (int i = 0; i < 1; i++);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ test.execute()
|
|||
test.file_grep(test.stats, r'NBA, variables using ShadowVar scheme\s+(\d+)', 1)
|
||||
test.file_grep(test.stats, r'NBA, variables using ShadowVarMasked scheme\s+(\d+)', 2)
|
||||
test.file_grep(test.stats, r'NBA, variables using FlagUnique scheme\s+(\d+)', 1)
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 0)
|
||||
test.file_grep_not(test.stats, r'Warnings, Suppressed BLKANDNBLK')
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ test.execute()
|
|||
test.file_grep(test.stats, r'NBA, variables using ShadowVar scheme\s+(\d+)', 1)
|
||||
test.file_grep(test.stats, r'NBA, variables using ShadowVarMasked scheme\s+(\d+)', 2)
|
||||
test.file_grep(test.stats, r'NBA, variables using FlagUnique scheme\s+(\d+)', 1)
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Warnings, Suppressed BLKANDNBLK\s+(\d+)', 2)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ test.compile(v_flags2=['+define+TEST_FULL', '--stats'])
|
|||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Iterations\s+(\d+)', 11)
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 4)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 107)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 27)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ test.compile(verilator_flags2=['--unroll-count 4 --unroll-stmts 9999 --stats -DT
|
|||
make_top_shell=False,
|
||||
make_main=False)
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 1)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Pragma unroll_disable\s+(\d+)', 4)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 0)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ test.compile(verilator_flags2=['--unroll-count 4 --unroll-stmts 9999 --stats -DT
|
|||
make_top_shell=False,
|
||||
make_main=False)
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 5)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 9)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 45)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ test.compile(verilator_flags2=['--unroll-count 4 --unroll-stmts 9999 --stats -DT
|
|||
make_top_shell=False,
|
||||
make_main=False)
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Unrolled Loops\s+(\d+)', 3)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 3)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 9)
|
||||
test.file_grep(test.stats,
|
||||
r'Optimizations, Loop unrolling, Failed - reached --unroll-count\s+(\d+)', 2)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
loop_0 0
|
||||
loop_0 1
|
||||
loop_0 2
|
||||
loop_1 0 5
|
||||
loop_1 2 6
|
||||
loop_1 4 7
|
||||
loop_1 6 8
|
||||
loop_1 8 9
|
||||
loop_2 0 5
|
||||
loop_2 1 5
|
||||
loop_2 2 5
|
||||
loop_2 3 5
|
||||
loop_2 4 5
|
||||
loop_3 4 0
|
||||
loop_3 3 0
|
||||
loop_3 2 0
|
||||
loop_3 1 0
|
||||
loop_3 0 0
|
||||
loop_4
|
||||
loop_5 1
|
||||
loop_5 2
|
||||
loop_5 3
|
||||
loop_5 4
|
||||
loop_5 5
|
||||
loop_5 6
|
||||
loop_5 7
|
||||
loop_6 0
|
||||
loop_6 1
|
||||
loop_6 2
|
||||
loop_6 3
|
||||
loop_6 4
|
||||
loop_6 5
|
||||
stopping loop_6
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
test.compile(verilator_flags2=["--binary", "--stats"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Failed - contains fork\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Failed - infinite loop\s+(\d+)', 0)
|
||||
test.file_grep(test.stats,
|
||||
r'Optimizations, Loop unrolling, Failed - loop test in sub-statement\s+(\d+)', 0)
|
||||
test.file_grep(test.stats,
|
||||
r'Optimizations, Loop unrolling, Failed - reached --unroll-count\s+(\d+)', 0)
|
||||
test.file_grep(test.stats,
|
||||
r'Optimizations, Loop unrolling, Failed - reached --unroll-stmts\s+(\d+)', 0)
|
||||
test.file_grep(test.stats,
|
||||
r'Optimizations, Loop unrolling, Failed - unknown loop condition\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Pragma unroll_disable\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 6)
|
||||
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 40)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
|
||||
int static_loop_cond;
|
||||
|
||||
function logic f_loop_cond();
|
||||
return ++static_loop_cond < 8;
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
// Basic loop
|
||||
for (int i = 0; i < 3; ++i) begin : loop_0
|
||||
$display("loop_0 %0d", i);
|
||||
end
|
||||
// Loop with 2 init/step
|
||||
for (int i = 0, j = 5; i < j; i += 2, j += 1) begin : loop_1
|
||||
$display("loop_1 %0d %0d", i, j);
|
||||
end
|
||||
// While loop with non-trivial init
|
||||
begin
|
||||
automatic int i = 0;
|
||||
automatic int j = 5; // Not a variable
|
||||
while (i < j) begin : loop_2
|
||||
$display("loop_2 %0d %0d", i++, j);
|
||||
end
|
||||
end
|
||||
// Do loop with non-trivial init
|
||||
begin
|
||||
automatic int i = 5;
|
||||
automatic int j = 0; // Not a variable
|
||||
do begin : loop_3
|
||||
$display("loop_3 %0d %0d", --i, j);
|
||||
end while (i > j);
|
||||
end
|
||||
// Do loop that executes once - replaced by V3Const, not unrolled
|
||||
do begin: loop_4
|
||||
$display("loop_4");
|
||||
end while(0);
|
||||
// Loop with inlined function as condition
|
||||
static_loop_cond = 0;
|
||||
while (f_loop_cond()) begin : loop_5
|
||||
$display("loop_5 %0d", static_loop_cond);
|
||||
end
|
||||
// Self disabling loop in via 'then' branch of 'if'
|
||||
begin
|
||||
automatic logic found = 0;
|
||||
for (int i = 0; i < 10; ++i) begin : loop_6
|
||||
if (!found) begin
|
||||
$display("loop_6 %0d", i);
|
||||
if (i == $c32("5")) begin // Unknown condition
|
||||
$display("stopping loop_6"); // This line is important
|
||||
found = 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
// Done
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -53,14 +53,6 @@
|
|||
</stmtexpr>
|
||||
</cfunc>
|
||||
<cfunc loc="d,11,8,11,9" name="_eval_initial__TOP">
|
||||
<var loc="d,28,11,28,14" name="t.all" dtype_id="10" vartype="string" origName="t__DOT__all"/>
|
||||
<creset loc="d,28,11,28,14">
|
||||
<varref loc="d,28,11,28,14" name="t.all" dtype_id="10"/>
|
||||
</creset>
|
||||
<var loc="d,52,17,52,18" name="t.unnamedblk1.e" dtype_id="2" vartype="my_t" origName="t__DOT__unnamedblk1__DOT__e"/>
|
||||
<creset loc="d,52,17,52,18">
|
||||
<varref loc="d,52,17,52,18" name="t.unnamedblk1.e" dtype_id="2"/>
|
||||
</creset>
|
||||
<var loc="d,49,120,49,121" name="__Vtemp_1" dtype_id="10" vartype="string" origName="__Vtemp_1"/>
|
||||
<assign loc="d,32,9,32,10" dtype_id="11">
|
||||
<const loc="d,32,11,32,14" name="4'h3" dtype_id="11"/>
|
||||
|
|
@ -520,80 +512,10 @@
|
|||
<stop loc="d,49,139,49,144"/>
|
||||
</begin>
|
||||
</if>
|
||||
<assign loc="d,51,11,51,12" dtype_id="10">
|
||||
<const loc="d,51,13,51,15" name="""" dtype_id="10"/>
|
||||
<varref loc="d,51,7,51,10" name="t.all" dtype_id="10"/>
|
||||
</assign>
|
||||
<assign loc="d,52,19,52,20" dtype_id="11">
|
||||
<const loc="d,52,23,52,28" name="4'h1" dtype_id="11"/>
|
||||
<varref loc="d,52,17,52,18" name="t.unnamedblk1.e" dtype_id="11"/>
|
||||
</assign>
|
||||
<while loc="d,52,7,52,10">
|
||||
<begin>
|
||||
<neq loc="d,52,32,52,34" dtype_id="8">
|
||||
<const loc="d,52,37,52,41" name="4'h4" dtype_id="11"/>
|
||||
<ccast loc="d,52,30,52,31" dtype_id="11">
|
||||
<varref loc="d,52,30,52,31" name="t.unnamedblk1.e" dtype_id="11"/>
|
||||
</ccast>
|
||||
</neq>
|
||||
</begin>
|
||||
<begin>
|
||||
<assign loc="d,53,14,53,15" dtype_id="10">
|
||||
<concatn loc="d,53,20,53,21" dtype_id="10">
|
||||
<varref loc="d,53,17,53,20" name="t.all" dtype_id="10"/>
|
||||
<arraysel loc="d,53,22,53,23" dtype_id="10">
|
||||
<varref loc="d,17,12,17,16" name="__Venumtab_enum_name1" dtype_id="16"/>
|
||||
<and loc="d,53,22,53,23" dtype_id="13">
|
||||
<const loc="d,53,22,53,23" name="32'h7" dtype_id="14"/>
|
||||
<ccast loc="d,53,22,53,23" dtype_id="13">
|
||||
<varref loc="d,53,22,53,23" name="t.unnamedblk1.e" dtype_id="13"/>
|
||||
</ccast>
|
||||
</and>
|
||||
</arraysel>
|
||||
</concatn>
|
||||
<varref loc="d,53,10,53,13" name="t.all" dtype_id="10"/>
|
||||
</assign>
|
||||
</begin>
|
||||
<begin>
|
||||
<assign loc="d,52,45,52,46" dtype_id="11">
|
||||
<arraysel loc="d,52,47,52,48" dtype_id="11">
|
||||
<varref loc="d,17,12,17,16" name="__Venumtab_enum_next1" dtype_id="12"/>
|
||||
<and loc="d,52,47,52,48" dtype_id="13">
|
||||
<const loc="d,52,47,52,48" name="32'h7" dtype_id="14"/>
|
||||
<ccast loc="d,52,47,52,48" dtype_id="13">
|
||||
<varref loc="d,52,47,52,48" name="t.unnamedblk1.e" dtype_id="13"/>
|
||||
</ccast>
|
||||
</and>
|
||||
</arraysel>
|
||||
<varref loc="d,52,43,52,44" name="t.unnamedblk1.e" dtype_id="11"/>
|
||||
</assign>
|
||||
</begin>
|
||||
</while>
|
||||
<assign loc="d,55,9,55,10" dtype_id="11">
|
||||
<const loc="d,55,13,55,17" name="4'h4" dtype_id="11"/>
|
||||
<varref loc="d,55,7,55,8" name="t.e" dtype_id="11"/>
|
||||
</assign>
|
||||
<assign loc="d,56,11,56,12" dtype_id="10">
|
||||
<concatn loc="d,56,17,56,18" dtype_id="10">
|
||||
<varref loc="d,56,14,56,17" name="t.all" dtype_id="10"/>
|
||||
<const loc="d,17,12,17,16" name=""E04"" dtype_id="10"/>
|
||||
</concatn>
|
||||
<varref loc="d,56,7,56,10" name="t.all" dtype_id="10"/>
|
||||
</assign>
|
||||
<if loc="d,57,10,57,12">
|
||||
<neqn loc="d,57,20,57,22" dtype_id="8">
|
||||
<const loc="d,57,24,57,35" name=""E01E03E04"" dtype_id="10"/>
|
||||
<varref loc="d,57,15,57,18" name="t.all" dtype_id="10"/>
|
||||
</neqn>
|
||||
<begin>
|
||||
<display loc="d,57,44,57,50" displaytype="$write">
|
||||
<sformatf loc="d,57,44,57,50" name="%%Error: t/t_enum_type_methods.v:57: got='%@' exp='E01E03E04' " dtype_id="10">
|
||||
<varref loc="d,57,123,57,126" name="t.all" dtype_id="10"/>
|
||||
</sformatf>
|
||||
</display>
|
||||
<stop loc="d,57,145,57,150"/>
|
||||
</begin>
|
||||
</if>
|
||||
</cfunc>
|
||||
<cfunc loc="a,0,0,0,0" name="_eval_final"/>
|
||||
<cfunc loc="a,0,0,0,0" name="_eval_settle"/>
|
||||
|
|
@ -1596,11 +1518,11 @@
|
|||
<const loc="d,11,8,11,9" name="1'h1" dtype_id="8"/>
|
||||
<varref loc="d,11,8,11,9" name="__VnbaContinue" dtype_id="8"/>
|
||||
</assign>
|
||||
<while loc="a,0,0,0,0">
|
||||
<begin>
|
||||
<varref loc="a,0,0,0,0" name="__VnbaContinue" dtype_id="8"/>
|
||||
</begin>
|
||||
<loop loc="a,0,0,0,0">
|
||||
<begin>
|
||||
<looptest loc="a,0,0,0,0">
|
||||
<varref loc="a,0,0,0,0" name="__VnbaContinue" dtype_id="8"/>
|
||||
</looptest>
|
||||
<if loc="a,0,0,0,0">
|
||||
<lt loc="a,0,0,0,0" dtype_id="8">
|
||||
<const loc="a,0,0,0,0" name="32'h64" dtype_id="14"/>
|
||||
|
|
@ -1639,11 +1561,11 @@
|
|||
<const loc="d,11,8,11,9" name="1'h1" dtype_id="8"/>
|
||||
<varref loc="d,11,8,11,9" name="__VactContinue" dtype_id="8"/>
|
||||
</assign>
|
||||
<while loc="a,0,0,0,0">
|
||||
<begin>
|
||||
<varref loc="a,0,0,0,0" name="__VactContinue" dtype_id="8"/>
|
||||
</begin>
|
||||
<loop loc="a,0,0,0,0">
|
||||
<begin>
|
||||
<looptest loc="a,0,0,0,0">
|
||||
<varref loc="a,0,0,0,0" name="__VactContinue" dtype_id="8"/>
|
||||
</looptest>
|
||||
<if loc="a,0,0,0,0">
|
||||
<lt loc="a,0,0,0,0" dtype_id="8">
|
||||
<const loc="a,0,0,0,0" name="32'h64" dtype_id="14"/>
|
||||
|
|
@ -1684,7 +1606,7 @@
|
|||
</begin>
|
||||
</if>
|
||||
</begin>
|
||||
</while>
|
||||
</loop>
|
||||
<if loc="a,0,0,0,0">
|
||||
<ccall loc="a,0,0,0,0" dtype_id="8" func="_eval_phase__nba"/>
|
||||
<begin>
|
||||
|
|
@ -1695,7 +1617,7 @@
|
|||
</begin>
|
||||
</if>
|
||||
</begin>
|
||||
</while>
|
||||
</loop>
|
||||
</cfunc>
|
||||
<cfunc loc="d,11,8,11,9" name="_eval_debug_assertions">
|
||||
<if loc="d,15,10,15,13">
|
||||
|
|
@ -1828,7 +1750,6 @@
|
|||
<const loc="d,17,12,17,16" name="32'h0" dtype_id="14"/>
|
||||
</range>
|
||||
</unpackarraydtype>
|
||||
<refdtype loc="d,52,12,52,16" id="22" name="my_t" sub_dtype_id="2"/>
|
||||
<basicdtype loc="d,23,23,23,24" id="9" name="logic" left="31" right="0" signed="true"/>
|
||||
<voiddtype loc="a,0,0,0,0" id="7"/>
|
||||
<basicdtype loc="d,11,8,11,9" id="6" name="VlTriggerVec"/>
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ test.files_identical(out_filename, test.golden_filename, 'logfile')
|
|||
test.file_grep(out_filename, r'<constpool')
|
||||
test.file_grep(out_filename, r'<inititem')
|
||||
test.file_grep(out_filename, r'<if')
|
||||
test.file_grep(out_filename, r'<while')
|
||||
test.file_grep(out_filename, r'<begin>') # for <if> and <while>
|
||||
test.file_grep(out_filename, r'<loop')
|
||||
test.file_grep(out_filename, r'<begin>') # for <if> and <loop>
|
||||
test.file_grep(out_filename, r' signed=') # for <basicdtype>
|
||||
test.file_grep(out_filename, r' func=') # for <ccall>
|
||||
|
||||
|
|
|
|||
|
|
@ -73,14 +73,14 @@
|
|||
<const loc="d,18,12,18,13" name="32'sh0" dtype_id="4"/>
|
||||
<varref loc="d,18,10,18,11" name="__Vfunc_vlvbound_test.foo__0__i" dtype_id="3"/>
|
||||
</assign>
|
||||
<while loc="d,18,5,18,8">
|
||||
<begin>
|
||||
<gts loc="d,18,18,18,19" dtype_id="5">
|
||||
<const loc="d,18,20,18,21" name="32'sh7" dtype_id="4"/>
|
||||
<varref loc="d,18,16,18,17" name="__Vfunc_vlvbound_test.foo__0__i" dtype_id="3"/>
|
||||
</gts>
|
||||
</begin>
|
||||
<loop loc="d,18,5,18,8">
|
||||
<begin>
|
||||
<looptest loc="d,18,16,18,17">
|
||||
<gts loc="d,18,18,18,19" dtype_id="5">
|
||||
<const loc="d,18,20,18,21" name="32'sh7" dtype_id="4"/>
|
||||
<varref loc="d,18,16,18,17" name="__Vfunc_vlvbound_test.foo__0__i" dtype_id="3"/>
|
||||
</gts>
|
||||
</looptest>
|
||||
<assign loc="d,19,14,19,15" dtype_id="5">
|
||||
<eq loc="d,19,31,19,33" dtype_id="5">
|
||||
<const loc="d,19,34,19,39" name="2'h0" dtype_id="6"/>
|
||||
|
|
@ -103,8 +103,6 @@
|
|||
</sel>
|
||||
</sel>
|
||||
</assign>
|
||||
</begin>
|
||||
<begin>
|
||||
<assign loc="d,18,24,18,26" dtype_id="3">
|
||||
<add loc="d,18,24,18,26" dtype_id="8">
|
||||
<const loc="d,18,24,18,26" name="32'h1" dtype_id="8"/>
|
||||
|
|
@ -113,7 +111,7 @@
|
|||
<varref loc="d,18,23,18,24" name="__Vfunc_vlvbound_test.foo__0__i" dtype_id="3"/>
|
||||
</assign>
|
||||
</begin>
|
||||
</while>
|
||||
</loop>
|
||||
<assign loc="d,21,5,21,11" dtype_id="2">
|
||||
<varref loc="d,21,12,21,15" name="__Vfunc_vlvbound_test.foo__0__ret" dtype_id="2"/>
|
||||
<varref loc="d,21,5,21,11" name="__Vfunc_vlvbound_test.foo__0__Vfuncout" dtype_id="2"/>
|
||||
|
|
@ -139,14 +137,14 @@
|
|||
<const loc="d,18,12,18,13" name="32'sh0" dtype_id="4"/>
|
||||
<varref loc="d,18,10,18,11" name="__Vfunc_vlvbound_test.foo__1__i" dtype_id="3"/>
|
||||
</assign>
|
||||
<while loc="d,18,5,18,8">
|
||||
<begin>
|
||||
<gts loc="d,18,18,18,19" dtype_id="5">
|
||||
<const loc="d,18,20,18,21" name="32'sh7" dtype_id="4"/>
|
||||
<varref loc="d,18,16,18,17" name="__Vfunc_vlvbound_test.foo__1__i" dtype_id="3"/>
|
||||
</gts>
|
||||
</begin>
|
||||
<loop loc="d,18,5,18,8">
|
||||
<begin>
|
||||
<looptest loc="d,18,16,18,17">
|
||||
<gts loc="d,18,18,18,19" dtype_id="5">
|
||||
<const loc="d,18,20,18,21" name="32'sh7" dtype_id="4"/>
|
||||
<varref loc="d,18,16,18,17" name="__Vfunc_vlvbound_test.foo__1__i" dtype_id="3"/>
|
||||
</gts>
|
||||
</looptest>
|
||||
<assign loc="d,19,14,19,15" dtype_id="5">
|
||||
<eq loc="d,19,31,19,33" dtype_id="5">
|
||||
<const loc="d,19,34,19,39" name="2'h0" dtype_id="6"/>
|
||||
|
|
@ -169,8 +167,6 @@
|
|||
</sel>
|
||||
</sel>
|
||||
</assign>
|
||||
</begin>
|
||||
<begin>
|
||||
<assign loc="d,18,24,18,26" dtype_id="3">
|
||||
<add loc="d,18,24,18,26" dtype_id="8">
|
||||
<const loc="d,18,24,18,26" name="32'h1" dtype_id="8"/>
|
||||
|
|
@ -179,7 +175,7 @@
|
|||
<varref loc="d,18,23,18,24" name="__Vfunc_vlvbound_test.foo__1__i" dtype_id="3"/>
|
||||
</assign>
|
||||
</begin>
|
||||
</while>
|
||||
</loop>
|
||||
<assign loc="d,21,5,21,11" dtype_id="2">
|
||||
<varref loc="d,21,12,21,15" name="__Vfunc_vlvbound_test.foo__1__ret" dtype_id="2"/>
|
||||
<varref loc="d,21,5,21,11" name="__Vfunc_vlvbound_test.foo__1__Vfuncout" dtype_id="2"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue