Internal cleanups towards recursive functions (#3267)

This commit is contained in:
Wilson Snyder 2022-01-04 20:19:58 -05:00
parent 4e5f30858b
commit 41a563bdc8
2 changed files with 51 additions and 12 deletions

View File

@ -39,6 +39,7 @@ private:
V3Hash hashNodeAndIterate(AstNode* nodep, bool hashDType, bool hashChildren, V3Hash hashNodeAndIterate(AstNode* nodep, bool hashDType, bool hashChildren,
std::function<void()>&& f) { std::function<void()>&& f) {
// See comments in visit(AstCFunc) about this breaking recursion
if (m_cacheInUser4 && nodep->user4()) { if (m_cacheInUser4 && nodep->user4()) {
return V3Hash(nodep->user4()); return V3Hash(nodep->user4());
} else { } else {
@ -393,6 +394,12 @@ private:
} }
virtual void visit(AstCFunc* nodep) override { virtual void visit(AstCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
// We might be in a recursive function, if so on *second* call
// here we need to break what would be an infinite loop.
nodep->user4(V3Hash(1).value()); // Set this "first" call
// So that a second call will then exit hashNodeAndIterate
// Having a constant in the hash just means the recursion will
// end, it shouldn't change the CFunc having a unique hash itself.
m_hash += nodep->isLoose(); m_hash += nodep->isLoose();
}); });
} }
@ -488,11 +495,12 @@ private:
public: public:
// CONSTRUCTORS // CONSTRUCTORS
explicit HasherVisitor(AstNode* nodep) HasherVisitor(AstNode* nodep)
: m_cacheInUser4{true} { : m_cacheInUser4{true} {
iterate(nodep); iterate(nodep);
} }
explicit HasherVisitor(const AstNode* nodep) class Uncached {};
HasherVisitor(const AstNode* nodep, Uncached)
: m_cacheInUser4{false} { : m_cacheInUser4{false} {
iterate(const_cast<AstNode*>(nodep)); iterate(const_cast<AstNode*>(nodep));
} }
@ -504,11 +512,11 @@ public:
// V3Hasher methods // V3Hasher methods
V3Hash V3Hasher::operator()(AstNode* nodep) const { V3Hash V3Hasher::operator()(AstNode* nodep) const {
if (!nodep->user4()) { HasherVisitor{nodep}; } if (!nodep->user4()) HasherVisitor{nodep};
return V3Hash(nodep->user4()); return V3Hash(nodep->user4());
} }
V3Hash V3Hasher::uncachedHash(const AstNode* nodep) { V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
const HasherVisitor visitor{nodep}; const HasherVisitor visitor{nodep, HasherVisitor::Uncached{}};
return visitor.finalHash(); return visitor.finalHash();
} }

View File

@ -140,7 +140,10 @@ public:
getFTaskVertex(nodep)->cFuncp(cfuncp); getFTaskVertex(nodep)->cFuncp(cfuncp);
} }
void checkPurity(AstNodeFTask* nodep) { checkPurity(nodep, getFTaskVertex(nodep)); } void checkPurity(AstNodeFTask* nodep) { checkPurity(nodep, getFTaskVertex(nodep)); }
private:
void checkPurity(AstNodeFTask* nodep, TaskBaseVertex* vxp) { void checkPurity(AstNodeFTask* nodep, TaskBaseVertex* vxp) {
if (nodep->recursive()) return; // Impure, but no warning
if (!vxp->pure()) { if (!vxp->pure()) {
nodep->v3warn( nodep->v3warn(
IMPURE, "Unsupported: External variable referenced by non-inlined function/task: " IMPURE, "Unsupported: External variable referenced by non-inlined function/task: "
@ -156,8 +159,6 @@ public:
checkPurity(nodep, static_cast<TaskBaseVertex*>(edgep->top())); checkPurity(nodep, static_cast<TaskBaseVertex*>(edgep->top()));
} }
} }
private:
TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) { TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) {
if (!nodep->user4p()) nodep->user4p(new TaskFTaskVertex(&m_callGraph, nodep)); if (!nodep->user4p()) nodep->user4p(new TaskFTaskVertex(&m_callGraph, nodep));
return static_cast<TaskFTaskVertex*>(nodep->user4u().toGraphVertex()); return static_cast<TaskFTaskVertex*>(nodep->user4u().toGraphVertex());
@ -209,6 +210,7 @@ private:
m_curVxp = getFTaskVertex(nodep); m_curVxp = getFTaskVertex(nodep);
if (nodep->dpiImport()) m_curVxp->noInline(true); if (nodep->dpiImport()) m_curVxp->noInline(true);
if (nodep->classMethod()) m_curVxp->noInline(true); // Until V3Task supports it if (nodep->classMethod()) m_curVxp->noInline(true); // Until V3Task supports it
if (nodep->recursive()) m_curVxp->noInline(true);
if (nodep->isConstructor()) { if (nodep->isConstructor()) {
m_curVxp->noInline(true); m_curVxp->noInline(true);
m_ctorp = nodep; m_ctorp = nodep;
@ -726,6 +728,24 @@ private:
return new AstCStmt(portp->fileline(), stmt); return new AstCStmt(portp->fileline(), stmt);
} }
void unlinkAndClone(AstNodeFTask* funcp, AstNode* nodep, bool withNext) {
UASSERT_OBJ(nodep, funcp, "null in function object clone");
VNRelinker relinkHandle;
if (withNext) {
nodep->unlinkFrBackWithNext(&relinkHandle);
} else {
nodep->unlinkFrBack(&relinkHandle);
}
if (funcp->recursive()) {
// Recursive functions require the original argument list to
// still be live for linking purposes.
// The old function gets clone, so that node pointers are mostly
// retained through the V3Task transformations
AstNode* const newp = nodep->cloneTree(withNext);
relinkHandle.relink(newp);
}
}
static AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix, static AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix,
const string& toSuffix) { const string& toSuffix) {
const string stmt = V3Task::assignInternalToDpi(portp, isPtr, frSuffix, toSuffix); const string stmt = V3Task::assignInternalToDpi(portp, isPtr, frSuffix, toSuffix);
@ -1149,7 +1169,7 @@ private:
if (ftaskNoInline || nodep->dpiExport()) { if (ftaskNoInline || nodep->dpiExport()) {
portp->funcReturn(false); // Converting return to 'outputs' portp->funcReturn(false); // Converting return to 'outputs'
} }
portp->unlinkFrBack(); unlinkAndClone(nodep, portp, false);
rtnvarp = portp; rtnvarp = portp;
rtnvarp->funcLocal(true); rtnvarp->funcLocal(true);
rtnvarp->name(rtnvarp->name() rtnvarp->name(rtnvarp->name()
@ -1260,7 +1280,7 @@ private:
} else { } else {
if (portp->isIO()) { if (portp->isIO()) {
// Move it to new function // Move it to new function
portp->unlinkFrBack(); unlinkAndClone(nodep, portp, false);
portp->funcLocal(true); portp->funcLocal(true);
cfuncp->addArgsp(portp); cfuncp->addArgsp(portp);
} else { } else {
@ -1282,10 +1302,21 @@ private:
if (rtnvarp) cfuncp->addArgsp(rtnvarp); if (rtnvarp) cfuncp->addArgsp(rtnvarp);
// Move body // Move body
AstNode* const bodysp = nodep->stmtsp(); AstNode* bodysp = nodep->stmtsp();
if (bodysp) { if (bodysp) {
bodysp->unlinkFrBackWithNext(); unlinkAndClone(nodep, bodysp, true);
cfuncp->addStmtsp(bodysp); AstBegin* const tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", bodysp};
VL_DANGLING(bodysp);
// If we cloned due to recursion, now need to rip out the ports
// (that remained in place) then got cloned
for (AstNode *nextp, *stmtp = tempp->stmtsp(); stmtp; stmtp = nextp) {
nextp = stmtp->nextp();
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO()) portp->unlinkFrBack();
}
}
if (tempp->stmtsp()) cfuncp->addStmtsp(tempp->stmtsp()->unlinkFrBackWithNext());
VL_DO_DANGLING(tempp->deleteTree(), tempp);
} }
if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp, dpiFuncp); if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp, dpiFuncp);
@ -1455,7 +1486,7 @@ private:
if (visitp) iterateAndNextNull(visitp); if (visitp) iterateAndNextNull(visitp);
} }
virtual void visit(AstNodeFTask* nodep) override { virtual void visit(AstNodeFTask* nodep) override {
UINFO(4, " Inline " << nodep << endl); UINFO(4, " visitFTask " << nodep << endl);
VL_RESTORER(m_insMode); VL_RESTORER(m_insMode);
VL_RESTORER(m_insStmtp); VL_RESTORER(m_insStmtp);
m_insMode = IM_BEFORE; m_insMode = IM_BEFORE;