2012-04-12 21:08:20 -04:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for unroll nodes
|
|
|
|
|
//
|
2019-11-07 22:33:59 -05:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2024-01-01 03:19:59 -05:00
|
|
|
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you
|
2020-03-21 11:24:24 -04:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 17:07:57 -04:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 11:24:24 -04:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Unroll's Transformations:
|
2019-05-19 16:13:13 -04:00
|
|
|
// Note is called twice. Once on modules for GenFor unrolling,
|
|
|
|
|
// Again after V3Scope for normal for loop unrolling.
|
2008-06-09 21:25:10 -04:00
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
// Each module:
|
2019-05-19 16:13:13 -04:00
|
|
|
// 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
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-04 20:17:11 -04:00
|
|
|
|
2023-10-18 06:37:46 -04:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#include "V3Unroll.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
|
|
|
|
|
#include "V3Const.h"
|
2016-01-21 19:00:19 -05:00
|
|
|
#include "V3Simulate.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
#include "V3Stats.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2022-09-18 20:53:42 +01:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
|
|
|
|
// Unroll state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 13:56:40 -05:00
|
|
|
class UnrollVisitor final : public VNVisitor {
|
2006-08-26 11:35:28 +00:00
|
|
|
// STATE
|
2020-04-15 07:58:34 -04:00
|
|
|
AstVar* m_forVarp; // Iterator variable
|
2021-11-26 17:55:36 -05:00
|
|
|
const AstVarScope* m_forVscp; // Iterator variable scope (nullptr for generate pass)
|
2020-04-15 07:58:34 -04:00
|
|
|
AstConst* m_varValuep; // Current value of loop
|
2021-11-26 17:55:36 -05:00
|
|
|
const AstNode* m_ignoreIncp; // Increment node to ignore
|
2020-04-15 07:58:34 -04:00
|
|
|
bool m_varModeCheck; // Just checking RHS assignments
|
|
|
|
|
bool m_varModeReplace; // Replacing varrefs
|
|
|
|
|
bool m_varAssignHit; // Assign var hit
|
|
|
|
|
bool m_generate; // Expand single generate For loop
|
|
|
|
|
string m_beginName; // What name to give begin iterations
|
|
|
|
|
VDouble0 m_statLoops; // Statistic tracking
|
|
|
|
|
VDouble0 m_statIters; // Statistic tracking
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2009-01-21 16:56:50 -05:00
|
|
|
// METHODS
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-07-30 11:52:35 -04:00
|
|
|
bool cantUnroll(AstNode* nodep, const char* reason) const {
|
2020-06-09 19:20:16 -04:00
|
|
|
if (m_generate)
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason);
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(3, " Can't Unroll: " << reason << " :" << nodep << endl);
|
2022-11-27 08:31:22 -05:00
|
|
|
// if (debug() >= 9) nodep->dumpTree("- cant: ");
|
2024-01-28 20:24:28 -05:00
|
|
|
V3Stats::addStatSum("Unrolling gave up, "s + reason, 1);
|
2019-05-19 16:13:13 -04:00
|
|
|
return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
2010-04-17 08:01:22 -04:00
|
|
|
bool bodySizeOverRecurse(AstNode* nodep, int& bodySize, int bodyLimit) {
|
2019-05-19 16:13:13 -04:00
|
|
|
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);
|
2010-04-17 08:01:22 -04:00
|
|
|
}
|
|
|
|
|
|
2021-11-13 13:50:44 -05:00
|
|
|
bool forUnrollCheck(
|
2021-11-26 16:05:07 -05:00
|
|
|
AstNode* const nodep,
|
2024-01-26 07:49:07 -05:00
|
|
|
const VOptionBool& unrollFull, // Pragma unroll_full, unroll_disable
|
2021-11-13 13:50:44 -05:00
|
|
|
AstNode* const initp, // Maybe under nodep (no nextp), or standalone (ignore nextp)
|
|
|
|
|
AstNode* const precondsp, AstNode* condp,
|
|
|
|
|
AstNode* const incp, // Maybe under nodep or in bodysp
|
|
|
|
|
AstNode* bodysp) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// To keep the IF levels low, we return as each test fails.
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " FOR Check " << nodep << endl);
|
|
|
|
|
if (initp) UINFO(6, " Init " << initp << endl);
|
|
|
|
|
if (precondsp) UINFO(6, " Pcon " << precondsp << endl);
|
|
|
|
|
if (condp) UINFO(6, " Cond " << condp << endl);
|
|
|
|
|
if (incp) UINFO(6, " Inc " << incp << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
|
2024-01-26 07:49:07 -05:00
|
|
|
if (unrollFull.isSetFalse()) return cantUnroll(nodep, "pragma unroll_disable");
|
|
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
// Initial value check
|
2021-11-13 13:50:44 -05:00
|
|
|
AstAssign* const initAssp = VN_CAST(initp, Assign);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!initAssp) return cantUnroll(nodep, "no initial assignment");
|
2020-04-15 07:58:34 -04:00
|
|
|
UASSERT_OBJ(!(initp->nextp() && initp->nextp() != nodep), nodep,
|
2019-07-06 12:57:50 -04:00
|
|
|
"initial assignment shouldn't be a list");
|
2020-04-15 07:58:34 -04:00
|
|
|
if (!VN_IS(initAssp->lhsp(), VarRef)) {
|
|
|
|
|
return cantUnroll(nodep, "no initial assignment to simple variable");
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
//
|
|
|
|
|
// Condition check
|
2019-07-06 12:57:50 -04:00
|
|
|
UASSERT_OBJ(!condp->nextp(), nodep, "conditional shouldn't be a list");
|
2019-05-19 16:13:13 -04:00
|
|
|
//
|
|
|
|
|
// Assignment of next value check
|
2021-11-13 13:50:44 -05:00
|
|
|
const AstAssign* const incAssp = VN_CAST(incp, Assign);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!incAssp) return cantUnroll(nodep, "no increment assignment");
|
2020-06-29 21:18:41 -04:00
|
|
|
if (incAssp->nextp()) return cantUnroll(nodep, "multiple increments");
|
2016-01-21 19:00:19 -05:00
|
|
|
|
2021-10-22 13:56:48 +01:00
|
|
|
m_forVarp = VN_AS(initAssp->lhsp(), VarRef)->varp();
|
|
|
|
|
m_forVscp = VN_AS(initAssp->lhsp(), VarRef)->varScopep();
|
2018-02-01 21:32:58 -05:00
|
|
|
if (VN_IS(nodep, GenFor) && !m_forVarp->isGenVar()) {
|
2020-11-18 21:03:23 -05:00
|
|
|
nodep->v3error("Non-genvar used in generate for: " << m_forVarp->prettyNameQ());
|
2020-04-15 07:58:34 -04:00
|
|
|
} else if (!VN_IS(nodep, GenFor) && m_forVarp->isGenVar()) {
|
2024-03-02 09:05:21 -05:00
|
|
|
nodep->v3error("Genvar not legal in non-generate for (IEEE 1800-2023 27.4): "
|
2020-11-18 21:03:23 -05:00
|
|
|
<< m_forVarp->prettyNameQ() << '\n'
|
2020-01-29 21:16:44 -05:00
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... Suggest move for loop upwards to generate-level scope.");
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
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.
|
2021-11-13 13:50:44 -05:00
|
|
|
const AstConst* const constInitp = VN_CAST(initAssp->rhsp(), Const);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!constInitp) return cantUnroll(nodep, "non-constant initializer");
|
2016-01-21 19:00:19 -05:00
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
//
|
|
|
|
|
// Now, make sure there's no assignment to this variable in the loop
|
|
|
|
|
m_varModeCheck = true;
|
|
|
|
|
m_varAssignHit = false;
|
|
|
|
|
m_ignoreIncp = incp;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(precondsp);
|
|
|
|
|
iterateAndNextNull(bodysp);
|
|
|
|
|
iterateAndNextNull(incp);
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varModeCheck = false;
|
2020-08-15 10:12:55 -04:00
|
|
|
m_ignoreIncp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
2016-01-21 19:00:19 -05:00
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
//
|
2020-04-15 07:58:34 -04:00
|
|
|
if (m_forVscp) {
|
|
|
|
|
UINFO(8, " Loop Variable: " << m_forVscp << endl);
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(8, " Loop Variable: " << m_forVarp << endl);
|
|
|
|
|
}
|
2022-11-27 08:31:22 -05:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- for: ");
|
2014-10-15 21:29:37 -04:00
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!m_generate) {
|
2021-11-13 13:50:44 -05:00
|
|
|
const AstAssign* const incpAssign = VN_AS(incp, Assign);
|
2020-04-15 07:58:34 -04:00
|
|
|
if (!canSimulate(incpAssign->rhsp())) {
|
|
|
|
|
return cantUnroll(incp, "Unable to simulate increment");
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!canSimulate(condp)) return cantUnroll(condp, "Unable to simulate condition");
|
|
|
|
|
|
|
|
|
|
// Check whether to we actually want to try and unroll.
|
|
|
|
|
int loops;
|
2024-01-26 07:49:07 -05:00
|
|
|
const int limit = v3Global.opt.unrollCountAdjusted(unrollFull, m_generate, false);
|
|
|
|
|
if (!countLoops(initAssp, condp, incp, limit, loops)) {
|
2019-05-19 16:13:13 -04:00
|
|
|
return cantUnroll(nodep, "Unable to simulate loop");
|
2020-04-15 07:58:34 -04:00
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
|
|
|
|
|
// Less than 10 statements in the body?
|
2024-01-26 07:49:07 -05:00
|
|
|
if (!unrollFull.isSetTrue()) {
|
|
|
|
|
int bodySize = 0;
|
|
|
|
|
int bodyLimit = v3Global.opt.unrollStmts();
|
|
|
|
|
if (loops > 0) bodyLimit = v3Global.opt.unrollStmts() / loops;
|
|
|
|
|
if (bodySizeOverRecurse(precondsp, bodySize /*ref*/, bodyLimit)
|
|
|
|
|
|| bodySizeOverRecurse(bodysp, bodySize /*ref*/, bodyLimit)
|
|
|
|
|
|| bodySizeOverRecurse(incp, bodySize /*ref*/, bodyLimit)) {
|
|
|
|
|
return cantUnroll(nodep, "too many statements");
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Finally, we can do it
|
2024-01-26 07:49:07 -05:00
|
|
|
if (!forUnroller(nodep, unrollFull, initAssp, condp, precondsp, incp, bodysp)) {
|
2019-05-19 16:13:13 -04:00
|
|
|
return cantUnroll(nodep, "Unable to unroll loop");
|
|
|
|
|
}
|
|
|
|
|
VL_DANGLING(nodep);
|
|
|
|
|
// Cleanup
|
|
|
|
|
return true;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
2020-04-15 07:58:34 -04:00
|
|
|
bool canSimulate(AstNode* nodep) {
|
2018-03-10 14:10:41 -05:00
|
|
|
SimulateVisitor simvis;
|
|
|
|
|
AstNode* clonep = nodep->cloneTree(true);
|
|
|
|
|
simvis.mainCheckTree(clonep);
|
2020-08-15 10:12:55 -04:00
|
|
|
VL_DO_CLEAR(pushDeletep(clonep), clonep = nullptr);
|
2018-03-10 14:10:41 -05:00
|
|
|
return simvis.optimizable();
|
2016-01-21 19:00:19 -05:00
|
|
|
}
|
|
|
|
|
|
2020-04-15 07:58:34 -04:00
|
|
|
bool simulateTree(AstNode* nodep, const V3Number* loopValue, AstNode* dtypep,
|
|
|
|
|
V3Number& outNum) {
|
2019-05-29 22:43:26 -04:00
|
|
|
AstNode* clonep = nodep->cloneTree(true);
|
2019-07-06 12:57:50 -04:00
|
|
|
UASSERT_OBJ(clonep, nodep, "Failed to clone tree");
|
2019-05-19 16:13:13 -04:00
|
|
|
if (loopValue) {
|
2022-11-20 13:11:01 -05:00
|
|
|
m_varValuep = new AstConst{nodep->fileline(), *loopValue};
|
2019-05-19 16:13:13 -04:00
|
|
|
// Iteration requires a back, so put under temporary node
|
2022-11-20 13:11:01 -05:00
|
|
|
AstBegin* tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", clonep};
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varModeReplace = true;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(tempp->stmtsp());
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varModeReplace = false;
|
2019-05-29 22:43:26 -04:00
|
|
|
clonep = tempp->stmtsp()->unlinkFrBackWithNext();
|
2024-05-08 08:36:24 -04:00
|
|
|
VL_DO_CLEAR(tempp->deleteTree(), tempp = nullptr);
|
2020-08-15 10:12:55 -04:00
|
|
|
VL_DO_CLEAR(pushDeletep(m_varValuep), m_varValuep = nullptr);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
SimulateVisitor simvis;
|
2019-05-29 22:43:26 -04:00
|
|
|
simvis.mainParamEmulate(clonep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!simvis.optimizable()) {
|
|
|
|
|
UINFO(3, "Unable to simulate" << endl);
|
2022-11-27 08:31:22 -05:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- _simtree: ");
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
2019-05-19 16:13:13 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Fetch the result
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number* resp = simvis.fetchNumberNull(clonep);
|
|
|
|
|
if (!resp) {
|
2019-05-19 16:13:13 -04:00
|
|
|
UINFO(3, "No number returned from simulation" << endl);
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
2019-05-19 16:13:13 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Patch up datatype
|
|
|
|
|
if (dtypep) {
|
2021-11-26 16:05:07 -05:00
|
|
|
AstConst new_con{clonep->fileline(), *resp};
|
2019-05-19 16:13:13 -04:00
|
|
|
new_con.dtypeFrom(dtypep);
|
|
|
|
|
outNum = new_con.num();
|
2021-01-02 21:43:13 -05:00
|
|
|
outNum.isSigned(dtypep->isSigned());
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
2019-05-19 16:13:13 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
2021-11-26 16:05:07 -05:00
|
|
|
outNum = *resp;
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(clonep->deleteTree(), clonep);
|
2019-05-19 16:13:13 -04:00
|
|
|
return true;
|
2016-01-21 19:00:19 -05:00
|
|
|
}
|
|
|
|
|
|
2020-04-15 07:58:34 -04:00
|
|
|
bool countLoops(AstAssign* initp, AstNode* condp, AstNode* incp, int max, int& outLoopsr) {
|
2019-05-19 16:13:13 -04:00
|
|
|
outLoopsr = 0;
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number loopValue{initp};
|
2020-08-15 10:12:55 -04:00
|
|
|
if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { //
|
2019-05-09 20:03:19 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-04-03 22:31:54 -04:00
|
|
|
while (true) {
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number res{initp};
|
2020-08-15 10:12:55 -04:00
|
|
|
if (!simulateTree(condp, &loopValue, nullptr, res)) { //
|
2019-05-19 16:13:13 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-04-15 07:58:34 -04:00
|
|
|
if (!res.isEqOne()) break;
|
2016-01-21 19:00:19 -05:00
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
outLoopsr++;
|
2016-01-21 19:00:19 -05:00
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
// Run inc
|
2021-11-13 13:50:44 -05:00
|
|
|
AstAssign* const incpass = VN_AS(incp, Assign);
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number newLoopValue{initp};
|
2019-05-09 20:03:19 -04:00
|
|
|
if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
loopValue.opAssign(newLoopValue);
|
2020-04-15 07:58:34 -04:00
|
|
|
if (outLoopsr > max) return false;
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
return true;
|
2016-01-21 19:00:19 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-26 07:49:07 -05:00
|
|
|
bool forUnroller(AstNode* nodep, const VOptionBool& unrollFull, AstAssign* initp,
|
|
|
|
|
AstNode* condp, AstNode* precondsp, AstNode* incp, AstNode* bodysp) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(9, "forUnroller " << nodep << endl);
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number loopValue{nodep};
|
2020-08-15 10:12:55 -04:00
|
|
|
if (!simulateTree(initp->rhsp(), nullptr, initp, loopValue)) { //
|
2019-05-19 16:13:13 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-15 10:12:55 -04:00
|
|
|
AstNode* stmtsp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
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
|
2019-11-15 18:24:55 -05:00
|
|
|
// needed if we have > 1 loop, as we're constant propagating it
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
if (precondsp) {
|
|
|
|
|
precondsp->unlinkFrBackWithNext();
|
2022-09-17 13:48:51 +01:00
|
|
|
stmtsp = AstNode::addNext(stmtsp, precondsp);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
if (bodysp) {
|
|
|
|
|
bodysp->unlinkFrBackWithNext();
|
2022-09-17 13:48:51 +01:00
|
|
|
stmtsp = AstNode::addNext(stmtsp, bodysp); // Maybe null if no body
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2018-02-01 21:32:58 -05:00
|
|
|
if (incp && !VN_IS(nodep, GenFor)) { // Generates don't need to increment loop index
|
2019-05-19 16:13:13 -04:00
|
|
|
incp->unlinkFrBackWithNext();
|
2022-09-17 13:48:51 +01:00
|
|
|
stmtsp = AstNode::addNext(stmtsp, incp); // Maybe null if no body
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
// Mark variable to disable some later warnings
|
|
|
|
|
m_forVarp->usedLoopIdx(true);
|
|
|
|
|
|
2020-08-15 10:12:55 -04:00
|
|
|
AstNode* newbodysp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
++m_statLoops;
|
|
|
|
|
if (stmtsp) {
|
2019-11-16 03:14:55 -05:00
|
|
|
int times = 0;
|
2020-04-03 22:31:54 -04:00
|
|
|
while (true) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(8, " Looping " << loopValue << endl);
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number res{nodep};
|
2020-08-15 10:12:55 -04:00
|
|
|
if (!simulateTree(condp, &loopValue, nullptr, res)) {
|
2019-05-19 16:13:13 -04:00
|
|
|
nodep->v3error("Loop unrolling failed.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!res.isEqOne()) {
|
|
|
|
|
break; // Done with the loop
|
2020-04-15 07:58:34 -04:00
|
|
|
} else {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Replace iterator values with constant.
|
|
|
|
|
AstNode* oneloopp = stmtsp->cloneTree(true);
|
|
|
|
|
|
2022-11-20 13:11:01 -05:00
|
|
|
m_varValuep = new AstConst{nodep->fileline(), loopValue};
|
2019-05-19 16:13:13 -04:00
|
|
|
|
|
|
|
|
// Iteration requires a back, so put under temporary node
|
|
|
|
|
if (oneloopp) {
|
2021-11-13 13:50:44 -05:00
|
|
|
AstBegin* const tempp
|
2022-11-20 13:11:01 -05:00
|
|
|
= new AstBegin{oneloopp->fileline(), "[EditWrapper]", oneloopp};
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varModeReplace = true;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(tempp->stmtsp());
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varModeReplace = false;
|
|
|
|
|
oneloopp = tempp->stmtsp()->unlinkFrBackWithNext();
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
if (m_generate) {
|
2021-06-20 18:32:57 -04:00
|
|
|
const string index = AstNode::encodeNumber(m_varValuep->toSInt());
|
|
|
|
|
const string nname = m_beginName + "__BRA__" + index + "__KET__";
|
2022-11-20 13:11:01 -05:00
|
|
|
oneloopp = new AstBegin{oneloopp->fileline(), nname, oneloopp, true};
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2020-08-15 10:12:55 -04:00
|
|
|
VL_DO_CLEAR(pushDeletep(m_varValuep), m_varValuep = nullptr);
|
2020-04-15 07:58:34 -04:00
|
|
|
if (newbodysp) {
|
|
|
|
|
newbodysp->addNext(oneloopp);
|
|
|
|
|
} else {
|
|
|
|
|
newbodysp = oneloopp;
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
|
|
|
|
|
++m_statIters;
|
2024-01-26 07:49:07 -05:00
|
|
|
const int limit
|
|
|
|
|
= v3Global.opt.unrollCountAdjusted(unrollFull, m_generate, false);
|
|
|
|
|
if (++times / 3 > limit) {
|
2020-04-15 07:58:34 -04:00
|
|
|
nodep->v3error(
|
|
|
|
|
"Loop unrolling took too long;"
|
2024-01-26 07:49:07 -05:00
|
|
|
" probably this is an infinite loop, "
|
|
|
|
|
" or use /*verilator unroll_full*/, or set --unroll-count above "
|
|
|
|
|
<< times);
|
2019-05-19 16:13:13 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2019-05-19 16:13:13 -04:00
|
|
|
// loopValue += valInc
|
2021-11-13 13:50:44 -05:00
|
|
|
AstAssign* const incpass = VN_AS(incp, Assign);
|
2021-11-26 16:05:07 -05:00
|
|
|
V3Number newLoopValue{nodep};
|
2019-05-09 20:03:19 -04:00
|
|
|
if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) {
|
|
|
|
|
nodep->v3error("Loop unrolling failed");
|
2019-05-19 16:13:13 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
loopValue.opAssign(newLoopValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-15 18:24:55 -05:00
|
|
|
if (!newbodysp) { // initp might have effects after the loop
|
2020-08-15 10:12:55 -04:00
|
|
|
newbodysp = initp; // Maybe nullptr
|
|
|
|
|
initp = nullptr;
|
2019-11-15 18:24:55 -05:00
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
// Replace the FOR()
|
2020-04-15 07:58:34 -04:00
|
|
|
if (newbodysp) {
|
|
|
|
|
nodep->replaceWith(newbodysp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2021-02-21 21:25:21 -05:00
|
|
|
if (bodysp) VL_DO_DANGLING(pushDeletep(bodysp), bodysp);
|
|
|
|
|
if (precondsp) VL_DO_DANGLING(pushDeletep(precondsp), precondsp);
|
|
|
|
|
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
|
|
|
|
|
if (incp && !incp->backp()) VL_DO_DANGLING(pushDeletep(incp), incp);
|
2022-11-27 08:31:22 -05:00
|
|
|
if (debug() >= 9 && newbodysp) newbodysp->dumpTree("- _new: ");
|
2019-05-19 16:13:13 -04:00
|
|
|
return true;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstWhile* nodep) override {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (m_varModeCheck || m_varModeReplace) {
|
|
|
|
|
} else {
|
|
|
|
|
// Constify before unroll call, as it may change what is underneath.
|
2020-04-15 07:58:34 -04:00
|
|
|
if (nodep->precondsp()) {
|
|
|
|
|
V3Const::constifyEdit(nodep->precondsp()); // precondsp may change
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change
|
|
|
|
|
// Grab initial value
|
2020-08-15 10:12:55 -04:00
|
|
|
AstNode* initp = nullptr; // Should be statement before the while.
|
2019-05-19 16:13:13 -04:00
|
|
|
if (nodep->backp()->nextp() == nodep) initp = nodep->backp();
|
2021-02-21 21:25:21 -05:00
|
|
|
if (initp) VL_DO_DANGLING(V3Const::constifyEdit(initp), initp);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (nodep->backp()->nextp() == nodep) initp = nodep->backp();
|
|
|
|
|
// Grab assignment
|
2020-08-15 10:12:55 -04:00
|
|
|
AstNode* incp = nullptr; // Should be last statement
|
2022-09-15 19:43:56 +01:00
|
|
|
AstNode* stmtsp = nodep->stmtsp();
|
2019-05-19 16:13:13 -04:00
|
|
|
if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp());
|
2020-02-03 23:21:56 -05:00
|
|
|
// cppcheck-suppress duplicateCondition
|
2020-04-15 07:58:34 -04:00
|
|
|
if (nodep->incsp()) {
|
|
|
|
|
incp = nodep->incsp();
|
|
|
|
|
} else {
|
2022-09-15 19:43:56 +01:00
|
|
|
for (incp = nodep->stmtsp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
2020-04-15 07:58:34 -04:00
|
|
|
if (incp) VL_DO_DANGLING(V3Const::constifyEdit(incp), incp);
|
|
|
|
|
// Again, as may have changed
|
2022-09-15 19:43:56 +01:00
|
|
|
stmtsp = nodep->stmtsp();
|
|
|
|
|
for (incp = nodep->stmtsp(); incp && incp->nextp(); incp = incp->nextp()) {}
|
|
|
|
|
if (incp == stmtsp) stmtsp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
// And check it
|
2024-01-26 07:49:07 -05:00
|
|
|
if (forUnrollCheck(nodep, nodep->unrollFull(), initp, nodep->precondsp(),
|
|
|
|
|
nodep->condp(), incp, stmtsp)) {
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2006-09-05 20:06:23 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstGenFor* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
if (!m_generate || m_varModeReplace) {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
} // else V3Param will recursively call each for loop to be unrolled for us
|
|
|
|
|
if (m_varModeCheck || m_varModeReplace) {
|
|
|
|
|
} else {
|
|
|
|
|
// 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.
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
2024-01-26 07:49:07 -05:00
|
|
|
} else if (forUnrollCheck(nodep, VOptionBool{}, nodep->initsp(), nullptr,
|
|
|
|
|
nodep->condp(), nodep->incsp(), nodep->stmtsp())) {
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Did replacement
|
2019-05-19 16:13:13 -04:00
|
|
|
} else {
|
|
|
|
|
nodep->v3error("For loop doesn't have genvar index, or is malformed");
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeFor* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
if (m_generate) { // Ignore for's when expanding genfor's
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
} else {
|
2022-10-22 13:45:48 -04:00
|
|
|
nodep->v3fatalSrc("V3Begin should have removed standard FORs");
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-09-05 20:06:23 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
if (m_varModeCheck && nodep->varp() == m_forVarp && nodep->varScopep() == m_forVscp
|
2020-11-07 10:37:55 -05:00
|
|
|
&& nodep->access().isWriteOrRW()) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(8, " Itervar assigned to: " << nodep << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varAssignHit = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 07:58:34 -04:00
|
|
|
if (m_varModeReplace && nodep->varp() == m_forVarp && nodep->varScopep() == m_forVscp
|
2020-11-01 16:59:23 -05:00
|
|
|
&& nodep->access().isReadOnly()) {
|
2021-11-13 13:50:44 -05:00
|
|
|
AstNode* const newconstp = m_varValuep->cloneTree(false);
|
2019-05-19 16:13:13 -04:00
|
|
|
nodep->replaceWith(newconstp);
|
2024-05-08 08:36:24 -04:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNode* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
if (m_varModeCheck && nodep == m_ignoreIncp) {
|
|
|
|
|
// Ignore subtree that is the increment
|
|
|
|
|
} else {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-09-12 07:22:22 -04:00
|
|
|
// CONSTRUCTORS
|
2019-01-06 16:56:56 -05:00
|
|
|
UnrollVisitor() { init(false, ""); }
|
2022-09-16 11:22:11 +01:00
|
|
|
~UnrollVisitor() override {
|
2019-01-06 16:56:56 -05:00
|
|
|
V3Stats::addStatSum("Optimizations, Unrolled Loops", m_statLoops);
|
|
|
|
|
V3Stats::addStatSum("Optimizations, Unrolled Iterations", m_statIters);
|
|
|
|
|
}
|
2019-09-12 07:22:22 -04:00
|
|
|
// METHODS
|
2019-01-06 16:56:56 -05:00
|
|
|
void init(bool generate, const string& beginName) {
|
2020-08-15 10:12:55 -04:00
|
|
|
m_forVarp = nullptr;
|
|
|
|
|
m_forVscp = nullptr;
|
|
|
|
|
m_varValuep = nullptr;
|
|
|
|
|
m_ignoreIncp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
m_varModeCheck = false;
|
|
|
|
|
m_varModeReplace = false;
|
2018-06-14 18:59:24 -04:00
|
|
|
m_varAssignHit = false;
|
2019-05-19 16:13:13 -04:00
|
|
|
m_generate = generate;
|
|
|
|
|
m_beginName = beginName;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2019-01-06 16:56:56 -05:00
|
|
|
void process(AstNode* nodep, bool generate, const string& beginName) {
|
|
|
|
|
init(generate, beginName);
|
|
|
|
|
iterate(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Unroll class functions
|
|
|
|
|
|
2020-04-15 07:58:34 -04:00
|
|
|
UnrollStateful::UnrollStateful()
|
2020-08-16 09:55:36 -04:00
|
|
|
: m_unrollerp{new UnrollVisitor} {}
|
2019-01-06 16:56:56 -05:00
|
|
|
UnrollStateful::~UnrollStateful() { delete m_unrollerp; }
|
|
|
|
|
|
|
|
|
|
void UnrollStateful::unrollGen(AstNodeFor* nodep, const string& beginName) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(5, __FUNCTION__ << ": " << endl);
|
2019-01-06 16:56:56 -05:00
|
|
|
m_unrollerp->process(nodep, true, beginName);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 07:58:34 -04:00
|
|
|
void UnrollStateful::unrollAll(AstNetlist* nodep) { m_unrollerp->process(nodep, false, ""); }
|
2019-01-06 16:56:56 -05:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
void V3Unroll::unrollAll(AstNetlist* nodep) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 12:57:50 -05:00
|
|
|
{
|
2019-01-06 16:56:56 -05:00
|
|
|
UnrollStateful unroller;
|
|
|
|
|
unroller.unrollAll(nodep);
|
2018-03-10 12:57:50 -05:00
|
|
|
} // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("unroll", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|