Split procedures to better respect --output-split-cfuncs (#2942)

CFuncs only used to be split at procedure (always/initial/final block),
which on occasion can still yield huge output files if they have large
procedures. This patch make CFuncs split at statement boundaries within
procedures. This has the potential to help a lot, but still does not
help if there are huge statements within procedures.
This commit is contained in:
Geza Lore 2021-05-11 12:44:07 +01:00 committed by GitHub
parent 1a6378291a
commit 1422c23434
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 36 deletions

View File

@ -37,6 +37,7 @@ Verilator 4.202 2021-04-24
* Support overlaps in priority case statements (#2864). [Rupert Swarbrick]
* Support for null ports (#2875). [Udi Finkelstein]
* Optimize large lookup tables to static data (#2925). [Geza Lore]
* Split always blocks to better respect --output-split-cfuncs. [Geza Lore]
* Fix class unpacked-array compile error (#2774). [Iru Cai]
* Fix scope types in FST and VCD traces (#2805). [Alex Torregrosa]
* Fix exceeding command-line ar limit (#2834). [Yinan Xu]

View File

@ -1723,50 +1723,73 @@ void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* d
AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp,
AstCFunc*& newFuncpr, int& newStmtsr) {
AstActive* activep = nullptr;
AstScope* scopep = lvertexp->scopep();
AstSenTree* domainp = lvertexp->domainp();
AstScope* const scopep = lvertexp->scopep();
AstSenTree* const domainp = lvertexp->domainp();
AstNode* nodep = lvertexp->nodep();
AstNodeModule* modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
AstNodeModule* const modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
UASSERT(modp, "nullptr");
if (VN_IS(nodep, SenTree)) {
// Just ignore sensitivities, we'll deal with them when we move statements that need them
} else { // Normal logic
// Make or borrow a CFunc to contain the new statements
if (v3Global.opt.profCFuncs()
|| (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
// Put every statement into a unique function to ease profiling or reduce function size
newFuncpr = nullptr;
}
if (!newFuncpr && domainp != m_deleteDomainp) {
string name = cfuncName(modp, domainp, scopep, nodep);
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
newFuncpr->argTypes(EmitCBaseVisitor::symClassVar());
newFuncpr->symProlog(true);
newStmtsr = 0;
if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true);
scopep->addActivep(newFuncpr);
// Where will we be adding the call?
activep = new AstActive(nodep->fileline(), name, domainp);
// Add a top call to it
AstCCall* callp = new AstCCall(nodep->fileline(), newFuncpr);
callp->argTypes("vlSymsp");
activep->addStmtsp(callp);
UINFO(6, " New " << newFuncpr << endl);
// Move the logic into a CFunc
nodep->unlinkFrBack();
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
// procedures. Everything else is handled in one go
AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure);
if (procp && !v3Global.opt.profCFuncs()) {
nodep = procp->bodysp();
pushDeletep(procp);
}
// Move the logic to the function we're creating
nodep->unlinkFrBack();
if (domainp == m_deleteDomainp) {
UINFO(4, " Ordering deleting pre-settled " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
newFuncpr->addStmtsp(nodep);
if (v3Global.opt.outputSplitCFuncs()) {
// Add in the number of nodes we're adding
EmitCBaseCounterVisitor visitor(nodep);
newStmtsr += visitor.count();
while (nodep) {
// Make or borrow a CFunc to contain the new statements
if (v3Global.opt.profCFuncs()
|| (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
// Put every statement into a unique function to ease profiling or reduce function
// size
newFuncpr = nullptr;
}
if (!newFuncpr && domainp != m_deleteDomainp) {
const string name = cfuncName(modp, domainp, scopep, nodep);
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
newFuncpr->argTypes(EmitCBaseVisitor::symClassVar());
newFuncpr->symProlog(true);
newStmtsr = 0;
if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true);
scopep->addActivep(newFuncpr);
// Create top call to it
AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr);
callp->argTypes("vlSymsp");
// Where will we be adding the call?
AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp);
newActivep->addStmtsp(callp);
if (!activep) {
activep = newActivep;
} else {
activep->addNext(newActivep);
}
UINFO(6, " New " << newFuncpr << endl);
}
AstNode* const nextp = nodep->nextp();
// When processing statements in a procedure, unlink the current statement
if (nodep->backp()) nodep->unlinkFrBack();
if (domainp == m_deleteDomainp) {
UINFO(4, " Ordering deleting pre-settled " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
newFuncpr->addStmtsp(nodep);
if (v3Global.opt.outputSplitCFuncs()) {
// Add in the number of nodes we're adding
EmitCBaseCounterVisitor visitor(nodep);
newStmtsr += visitor.count();
}
}
nodep = nextp;
}
}
return activep;