From 1c0af6c7bc7c5e4c1a576b41502e9c0bdc14277b Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 12 Nov 2023 16:01:07 +0000 Subject: [PATCH] Reduce peak memory use of V3Subst (#4687) V3Subst is currently the pass responsible for peak memory usage. This patch saves ~16% of peak memory on OpenTitan. 2 changes: - It is actually safe to delete the substituted expressions immediately, but this is the lesser contribution - More importantly, we only ever substitute STMTTEMP variables, which are always defined within the same CFunc, so we can limit the scope of the optimization to CFunc. This allows us to reclaim the SubstVarEntry structures at the end of every CFunc, rather than at the end of the whole pass, which gives us most of the memory savings. Generated output is identical --- src/V3Subst.cpp | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index 7c2465dad..b569f3390 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -219,15 +219,15 @@ class SubstVisitor final : public VNVisitor { private: // NODE STATE // Passed to SubstUseVisitor - // AstVar::user1p -> SubstVar* for usage var, 0=not set yet + // AstVar::user1p -> SubstVar* for usage var, 0=not set yet. Only under CFunc. // AstVar::user2 -> int step number for last assignment, 0=not set yet - const VNUser1InUse m_inuser1; const VNUser2InUse m_inuser2; // STATE - std::vector m_entryps; // Nodes to delete when we are finished + std::deque m_entries; // Nodes to delete when we are finished int m_ops = 0; // Number of operators on assign rhs int m_assignStep = 0; // Assignment number to determine var lifetime + const AstCFunc* m_funcp = nullptr; // Current function we are under VDouble0 m_statSubsts; // Statistic tracking enum { @@ -237,21 +237,18 @@ private: // METHODS SubstVarEntry* getEntryp(AstVarRef* nodep) { - if (!nodep->varp()->user1p()) { - SubstVarEntry* const entryp = new SubstVarEntry{nodep->varp()}; - m_entryps.push_back(entryp); - nodep->varp()->user1p(entryp); - return entryp; - } else { - SubstVarEntry* const entryp - = reinterpret_cast(nodep->varp()->user1p()); - return entryp; + AstVar* const varp = nodep->varp(); + if (!varp->user1p()) { + m_entries.emplace_back(varp); + varp->user1p(&m_entries.back()); } + return varp->user1u().to(); } bool isSubstVar(AstVar* nodep) { return nodep->isStatementTemp() && !nodep->noSubst(); } // VISITORS void visit(AstNodeAssign* nodep) override { + if (!m_funcp) return; VL_RESTORER(m_ops); m_ops = 0; m_assignStep++; @@ -295,10 +292,11 @@ private: } if (debug() > 5) newp->dumpTree("- w_new: "); nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); + VL_DO_DANGLING(nodep->deleteTree(), nodep); ++m_statSubsts; } void visit(AstWordSel* nodep) override { + if (!m_funcp) return; iterate(nodep->rhsp()); AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef); const AstConst* const constp = VN_CAST(nodep->rhsp(), Const); @@ -324,6 +322,7 @@ private: } } void visit(AstVarRef* nodep) override { + if (!m_funcp) return; // Any variable if (nodep->access().isWriteOrRW()) { m_assignStep++; @@ -353,13 +352,19 @@ private: } void visit(AstVar*) override {} void visit(AstConst*) override {} - void visit(AstModule* nodep) override { - ++m_ops; - if (!nodep->isSubstOptimizable()) m_ops = SUBST_MAX_OPS_NA; + + void visit(AstCFunc* nodep) override { + UASSERT_OBJ(!m_funcp, nodep, "Should not nest"); + UASSERT_OBJ(m_entries.empty(), nodep, "References outside functions"); + VL_RESTORER(m_funcp); + m_funcp = nodep; + + const VNUser1InUse m_inuser1; iterateChildren(nodep); - // Reduce peak memory usage by reclaiming the edited AstNodes - doDeletes(); + for (SubstVarEntry& ip : m_entries) ip.deleteUnusedAssign(); + m_entries.clear(); } + void visit(AstNode* nodep) override { ++m_ops; if (!nodep->isSubstOptimizable()) m_ops = SUBST_MAX_OPS_NA; @@ -371,10 +376,7 @@ public: explicit SubstVisitor(AstNode* nodep) { iterate(nodep); } ~SubstVisitor() override { V3Stats::addStat("Optimizations, Substituted temps", m_statSubsts); - for (SubstVarEntry* ip : m_entryps) { - ip->deleteUnusedAssign(); - delete ip; - } + UASSERT(m_entries.empty(), "References outside functions"); } };