diff --git a/src/V3Ast.h b/src/V3Ast.h index c72e62b3a..1fd3940d3 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2280,7 +2280,6 @@ private: string m_name; // Name of variable string m_selfPointer; // Output code object pointer (e.g.: 'this') string m_classPrefix; // Output class prefix (i.e.: the part before ::) - bool m_hierThis = false; // m_selfPointer points to "this" function protected: AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access) @@ -2312,8 +2311,6 @@ public: void varp(AstVar* varp); AstVarScope* varScopep() const { return m_varScopep; } void varScopep(AstVarScope* varscp) { m_varScopep = varscp; } - bool hierThis() const { return m_hierThis; } - void hierThis(bool flag) { m_hierThis = flag; } string selfPointer() const { return m_selfPointer; } void selfPointer(const string& value) { m_selfPointer = value; } string selfPointerProtect(bool useSelfForThis) const; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index c2b82f85c..94ef71ce3 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2150,7 +2150,6 @@ public: } bool isClassMember() const { return varType() == AstVarType::MEMBER; } bool isStatementTemp() const { return (varType() == AstVarType::STMTTEMP); } - bool isMovableToBlock() const { return (varType() == AstVarType::BLOCKTEMP || isFuncLocal()); } bool isXTemp() const { return (varType() == AstVarType::XTEMP); } bool isParam() const { return (varType() == AstVarType::LPARAM || varType() == AstVarType::GPARAM); diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index f94813a06..54a131a75 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -48,25 +48,24 @@ private: AstCFunc* createDeepFunc(AstNode* nodep) { AstNRelinker relinkHandle; nodep->unlinkFrBack(&relinkHandle); - // Create function - string name = m_cfuncp->name() + "__deep" + cvtToStr(++m_deepNum); - AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, nullptr); + // Create sub function + AstScope* const scopep = m_cfuncp->scopep(); + const string name = m_cfuncp->name() + "__deep" + cvtToStr(++m_deepNum); + AstCFunc* const funcp = new AstCFunc(nodep->fileline(), name, scopep); funcp->slow(m_cfuncp->slow()); funcp->isStatic(m_cfuncp->isStatic()); funcp->isLoose(m_cfuncp->isLoose()); funcp->addStmtsp(nodep); - m_modp->addStmtp(funcp); - // Call it at the point where the body was removed from - AstCCall* callp = new AstCCall(nodep->fileline(), funcp); + scopep->addActivep(funcp); + // Call sub function at the point where the body was removed from + AstCCall* const callp = new AstCCall(nodep->fileline(), funcp); if (VN_IS(m_modp, Class)) { funcp->argTypes(EmitCBaseVisitor::symClassVar()); callp->argTypes("vlSymsp"); - } else if (!funcp->isStatic()) { - callp->selfPointer("this"); } UINFO(6, " New " << callp << endl); - // relinkHandle.relink(callp); + // Done return funcp; } diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 87b7e9564..2428d61a4 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -219,15 +219,17 @@ private: } virtual void visit(AstNodeVarRef* nodep) override { iterateChildren(nodep); + if (!nodep->varScopep()) { + UASSERT_OBJ(nodep->varp()->isFuncLocal(), nodep, + "unscoped reference can only appear to function locals at this point"); + return; + } // Convert the hierch name UINFO(9, " ref-in " << nodep << endl); UASSERT_OBJ(m_scopep, nodep, "Node not under scope"); const AstVar* const varp = nodep->varScopep()->varp(); const AstScope* const scopep = nodep->varScopep()->scopep(); - if (varp->isFuncLocal()) { - nodep->hierThis(true); - } else { - nodep->hierThis(scopep == m_scopep); + if (!varp->isFuncLocal()) { nodep->selfPointer(descopedSelfPointer(scopep)); nodep->classPrefix(descopedClassPrefix(scopep)); } @@ -282,7 +284,6 @@ public: void V3Descope::descopeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - v3Global.assertScoped(false); { DescopeVisitor visitor(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 12e10f41a..9ee00a9f7 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -15,9 +15,9 @@ //************************************************************************* // LOCALIZE TRANSFORMATIONS: // All modules: -// VAR(BLOCKTEMP... +// VARSCOPE(BLOCKTEMP... // if only referenced in a CFUNC, make it local to that CFUNC -// VAR(others +// VARSCOPE(others // if non-public, set before used, and in single CFUNC, make it local // //************************************************************************* @@ -33,18 +33,17 @@ #include //###################################################################### -// Localize base class +// LocalizeVisitor -class LocalizeBaseVisitor VL_NOT_FINAL : public AstNVisitor { -protected: +class LocalizeVisitor final : public AstNVisitor { +private: // NODE STATE - // Cleared on entire tree - // AstVar::user1p() -> CFunc which references the variable + // AstVar::user1p() -> First AstCFunc which references the variable // AstVar::user2() -> VarFlags. Flag state - // AstVar::user4() -> AstVarRef*. First place signal set; must be first assignment - - // METHODS - VL_DEBUG_FUNC; // Declare debug() + // AstVar::user4p() -> AstVarRef that writes whole variable, if first write ref. + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser4InUse m_inuser4; // TYPES union VarFlags { @@ -54,99 +53,89 @@ protected: int m_notOpt : 1; // NOT optimizable int m_notStd : 1; // NOT optimizable if a non-blocktemp signal int m_stdFuncAsn : 1; // Found simple assignment - int m_done : 1; // Removed }; // cppcheck-suppress unusedStructMember uint32_t m_flags; - explicit VarFlags(AstNode* nodep) { m_flags = nodep->user2(); } - void setNodeFlags(AstNode* nodep) { nodep->user2(m_flags); } + explicit VarFlags(AstVarScope* nodep) { m_flags = nodep->user2(); } + void setNodeFlags(AstVarScope* nodep) { nodep->user2(m_flags); } }; -}; - -//###################################################################### -// Localize class functions - -class LocalizeDehierVisitor final : public LocalizeBaseVisitor { -private: - // NODE STATE/TYPES - // See above - - // METHODS - virtual void visit(AstVarRef* nodep) override { - // cppcheck-suppress unreadVariable // cppcheck 1.90 bug - VarFlags flags(nodep->varp()); - if (flags.m_done) { - nodep->selfPointer(""); // Remove 'this' - nodep->hierThis(true); - } - } - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit LocalizeDehierVisitor(AstNetlist* nodep) { iterate(nodep); } - virtual ~LocalizeDehierVisitor() override = default; -}; - -//###################################################################### -// Localize class functions - -class LocalizeVisitor final : public LocalizeBaseVisitor { -private: - // NODE STATE/TYPES - // See above - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser4InUse m_inuser4; // STATE VDouble0 m_statLocVars; // Statistic tracking AstCFunc* m_cfuncp = nullptr; // Current active function - std::vector m_varps; // List of variables to consider for deletion + std::vector m_varScopeps; // List of variables to consider for localization + std::unordered_multimap + m_references; // VarRefs referencing the given VarScope // METHODS - void clearOptimizable(AstVar* nodep, const char* reason) { + VL_DEBUG_FUNC; // Declare debug() + + void clearOptimizable(AstVarScope* nodep, const char* reason) { UINFO(4, " NoOpt " << reason << " " << nodep << endl); VarFlags flags(nodep); flags.m_notOpt = true; flags.setNodeFlags(nodep); } - void clearStdOptimizable(AstVar* nodep, const char* reason) { + void clearStdOptimizable(AstVarScope* nodep, const char* reason) { UINFO(4, " NoStd " << reason << " " << nodep << endl); VarFlags flags(nodep); flags.m_notStd = true; flags.setNodeFlags(nodep); } - void moveVars() { - for (AstVar* nodep : m_varps) { - if (nodep->valuep()) clearOptimizable(nodep, "HasInitValue"); + void moveVarScopes() { + for (AstVarScope* const nodep : m_varScopeps) { + if (nodep->varp()->valuep()) clearOptimizable(nodep, "HasInitValue"); if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep, "NoStdAssign"); VarFlags flags(nodep); - if ((nodep->isMovableToBlock() // Blocktemp - || !flags.m_notStd) // Or used only in block + if ((nodep->varp()->varType() == AstVarType::BLOCKTEMP + || !flags.m_notStd) // Temporary Or used only in block && !flags.m_notOpt // Optimizable - && !nodep->isClassMember() && nodep->user1p()) { // Single cfunc - // We don't need to test for tracing; it would be in the tracefunc if it was needed - UINFO(4, " ModVar->BlkVar " << nodep << endl); + && !nodep->varp()->isClassMember() && // Statically exists in design hierarchy + nodep->user1p()) // Is under a CFunc + { + UINFO(4, "Localizing " << nodep << endl); ++m_statLocVars; - AstCFunc* newfuncp = VN_CAST(nodep->user1p(), CFunc); - nodep->unlinkFrBack(); - newfuncp->addInitsp(nodep); - // Done - flags.m_done = true; - flags.setNodeFlags(nodep); + AstCFunc* const funcp = VN_CAST(nodep->user1p(), CFunc); + // Yank the Var and VarScope from it's parent and schedule them for deletion + AstVar* const varp = nodep->varp(); + if (varp->backp()) { // Might have already unlinked this via another AstVarScope + pushDeletep(varp->unlinkFrBack()); + } + pushDeletep(nodep->unlinkFrBack()); + + // Create the new local variable. + const string newName + = nodep->scopep() == funcp->scopep() + ? varp->name() + : nodep->scopep()->nameDotless() + "__DOT__" + varp->name(); + + AstVar* const newVarp + = new AstVar(varp->fileline(), varp->varType(), newName, varp); + newVarp->funcLocal(true); + + // Fix up all the references + const auto er = m_references.equal_range(nodep); + for (auto it = er.first; it != er.second; ++it) { + AstVarRef* const refp = it->second; + refp->varScopep(nullptr); + refp->varp(newVarp); + } + + // Add the var to this function, and mark local + funcp->addInitsp(newVarp); } else { clearOptimizable(nodep, "NotDone"); } } - m_varps.clear(); + m_varScopeps.clear(); + m_references.clear(); } // VISITORS virtual void visit(AstNetlist* nodep) override { - iterateChildren(nodep); - moveVars(); + iterateChildrenConst(nodep); + moveVarScopes(); } virtual void visit(AstCFunc* nodep) override { UINFO(4, " CFUNC " << nodep << endl); @@ -157,7 +146,7 @@ private: searchFuncStmts(nodep->initsp()); searchFuncStmts(nodep->stmtsp()); searchFuncStmts(nodep->finalsp()); - iterateChildren(nodep); + iterateChildrenConst(nodep); } } void searchFuncStmts(AstNode* nodep) { @@ -166,60 +155,64 @@ private: // This could be more complicated; allow always-set under both branches of a IF. // If so, check for ArrayRef's and such, as they aren't acceptable. for (; nodep; nodep = nodep->nextp()) { - if (VN_IS(nodep, NodeAssign)) { - if (AstVarRef* varrefp = VN_CAST(VN_CAST(nodep, NodeAssign)->lhsp(), VarRef)) { + if (AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) { + if (AstVarRef* const varrefp = VN_CAST(assignp->lhsp(), VarRef)) { UASSERT_OBJ(varrefp->access().isWriteOrRW(), varrefp, - "LHS assignment not lvalue"); - if (!varrefp->varp()->user4p()) { + "LHS of assignment is not an lvalue"); + AstVarScope* const varScopep = varrefp->varScopep(); + if (!varScopep->user4p()) { UINFO(4, " FuncAsn " << varrefp << endl); - varrefp->varp()->user4p(varrefp); - VarFlags flags(varrefp->varp()); + varScopep->user4p(varrefp); + VarFlags flags(varScopep); flags.m_stdFuncAsn = true; - flags.setNodeFlags(varrefp->varp()); + flags.setNodeFlags(varScopep); } } } } } - virtual void visit(AstVar* nodep) override { - if (!nodep->isSigPublic() && !nodep->isPrimaryIO() - && !m_cfuncp) { // Not already inside a function + virtual void visit(AstVarScope* nodep) override { + if (!nodep->varp()->isPrimaryIO() // Not an IO the user wants to interact with + && !nodep->varp()->isSigPublic() // Not something the user wants to interact with + && !nodep->varp()->isFuncLocal() // Not already a function local (e.g.: argument) + && !nodep->varp()->isStatic() // Not a static variable + ) { UINFO(4, " BLKVAR " << nodep << endl); - m_varps.push_back(nodep); + m_varScopeps.push_back(nodep); } // No iterate; Don't want varrefs under it } virtual void visit(AstVarRef* nodep) override { - if (!VarFlags(nodep->varp()).m_notOpt) { + AstVarScope* const varScopep = nodep->varScopep(); + if (!VarFlags(varScopep).m_notOpt) { + // Remember the reference + m_references.emplace(varScopep, nodep); if (!m_cfuncp) { // Not in function, can't optimize // Perhaps impossible, but better safe - clearOptimizable(nodep->varp(), "BVnofunc"); // LCOV_EXCL_LINE + clearOptimizable(varScopep, "BVnofunc"); // LCOV_EXCL_LINE } else { - // If we're scoping down to it, it isn't really in the same block - if (!nodep->hierThis()) clearOptimizable(nodep->varp(), "HierRef"); // Allow a variable to appear in only a single function - AstNode* oldfunc = nodep->varp()->user1p(); + AstNode* const oldfunc = varScopep->user1p(); if (!oldfunc) { + // First encounter with this variable UINFO(4, " BVnewref " << nodep << endl); - nodep->varp()->user1p(m_cfuncp); // Remember where it was used - } else if (m_cfuncp == oldfunc) { - // Same usage - } else { + varScopep->user1p(m_cfuncp); // Remember where it was used + } else if (m_cfuncp != oldfunc) { // Used in multiple functions - clearOptimizable(nodep->varp(), "BVmultiF"); + clearOptimizable(varScopep, "BVmultiF"); } // First varref in function must be assignment found earlier - AstVarRef* firstasn = static_cast(nodep->varp()->user4p()); + const AstVarRef* const firstasn = VN_CAST(varScopep->user4p(), VarRef); if (firstasn && nodep != firstasn) { - clearStdOptimizable(nodep->varp(), "notFirstAsn"); - nodep->varp()->user4p(nullptr); + clearStdOptimizable(varScopep, "notFirstAsn"); + varScopep->user4p(nullptr); } } } // No iterate; Don't want varrefs under it } - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } public: // CONSTRUCTORS @@ -234,10 +227,6 @@ public: void V3Localize::localizeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { - LocalizeVisitor visitor(nodep); - // Fix up hiernames - LocalizeDehierVisitor dvisitor(nodep); - } // Destruct before checking + { LocalizeVisitor visitor(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("localize", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index a73fe58bf..187c01586 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -403,9 +403,6 @@ static void process() { if (v3Global.opt.trace()) V3Trace::traceAll(v3Global.rootp()); if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Scoped"); - - // Remove scopes; make varrefs/funccalls relative to current module - V3Descope::descopeAll(v3Global.rootp()); } //--MODULE OPTIMIZATIONS-------------- @@ -416,9 +413,15 @@ static void process() { V3DepthBlock::depthBlockAll(v3Global.rootp()); } - // Move BLOCKTEMPS from class to local variables + // Up until this point, all references must be scoped + v3Global.assertScoped(false); + + // Move variables from modules to function local variables where possible if (v3Global.opt.oLocalize()) V3Localize::localizeAll(v3Global.rootp()); + // Remove remaining scopes; make varrefs/funccalls relative to current module + V3Descope::descopeAll(v3Global.rootp()); + // Icache packing; combine common code in each module's functions into subroutines if (v3Global.opt.oCombine()) V3Combine::combineAll(v3Global.rootp()); }