mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
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:
+20
-44
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user