From 96336395d6fb5ac0d7c87de44a7e82e9e0837fd7 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 8 Jun 2026 14:45:49 +0100 Subject: [PATCH] Internals: Simplify V3CfgLiveVariables to work only in scoped mode No functional change --- src/V3Cfg.h | 10 ++--- src/V3CfgLiveVariables.cpp | 85 +++++++++++++------------------------- 2 files changed, 33 insertions(+), 62 deletions(-) diff --git a/src/V3Cfg.h b/src/V3Cfg.h index d4cf8a9af..f367a8366 100644 --- a/src/V3Cfg.h +++ b/src/V3Cfg.h @@ -559,13 +559,11 @@ public: namespace V3Cfg { -// Compute AstVars live on entry to given CFG. That is, variables that might +// Compute AstVarScopes live on entry to given CFG. That is, variables that might // be read before wholly assigned in the CFG. Returns nullptr if the analysis -// failed due to unhandled statements or data types involved in the CFG. -// On success, returns a vector of AstVar or AstVarScope nodes live on entry. -std::unique_ptr> liveVars(const CfgGraph&); - -// Same as liveVars, but return AstVarScopes insted +// failed due to unhandled statements involved in the CFG. The analysis is +// conservative and may return variables that are not actually live on entry. +// On success, returns a vector of AstVarScope nodes that might be live on entry. std::unique_ptr> liveVarScopes(const CfgGraph&); } //namespace V3Cfg diff --git a/src/V3CfgLiveVariables.cpp b/src/V3CfgLiveVariables.cpp index 3b6eeab70..79033af9d 100644 --- a/src/V3CfgLiveVariables.cpp +++ b/src/V3CfgLiveVariables.cpp @@ -25,25 +25,19 @@ #include "V3Ast.h" #include "V3Cfg.h" -#include #include -#include VL_DEFINE_DEBUG_FUNCTIONS; -template class CfgLiveVariables final : VNVisitorConst { - // TYPES - using Variable = std::conditional_t; - // State associted with each basic block struct BlockState final { // Variables used in block, before a complete assignment in the same block - std::unordered_set m_gen; + std::unordered_set m_gen; // Variables that are assigned a complete value in the basic block - std::unordered_set m_kill; - std::unordered_set m_liveIn; // Variables live on entry to the block - std::unordered_set m_liveOut; // Variables live on exit from the block + std::unordered_set m_kill; + std::unordered_set m_liveIn; // Variables live on entry to the block + std::unordered_set m_liveOut; // Variables live on exit from the block bool m_isOnWorkList = false; // Block is on work list bool m_wasProcessed = false; // Already processed at least once }; @@ -56,14 +50,6 @@ class CfgLiveVariables final : VNVisitorConst { bool m_abort = false; // Abort analysis - unhandled construct // METHODS - static Variable* getTarget(const AstNodeVarRef* refp) { - // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works - if VL_CONSTEXPR_CXX17 (T_Scoped) { - return reinterpret_cast(refp->varScopep()); - } else { - return reinterpret_cast(refp->varp()); - } - } // This is to match DFG, but can be extended independently - eventually should handle all static bool isSupportedPackedDType(const AstNodeDType* dtypep) { @@ -81,52 +67,41 @@ class CfgLiveVariables final : VNVisitorConst { } // Check and return if variable is incompatible - bool incompatible(Variable* varp) { - if (!isSupportedPackedDType(varp->dtypep())) return true; - const AstVar* astVarp = nullptr; - // TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works - if VL_CONSTEXPR_CXX17 (T_Scoped) { - astVarp = reinterpret_cast(varp)->varp(); - } else { - astVarp = reinterpret_cast(varp); - } - if (astVarp->ignoreSchedWrite()) return true; + bool incompatible(AstVarScope* vscp) { + if (!isSupportedPackedDType(vscp->dtypep())) return true; + if (vscp->varp()->ignoreSchedWrite()) return true; return false; } void updateGen(AstNode* nodep) { UASSERT_OBJ(!m_abort, nodep, "Doing useless work"); - m_abort = nodep->exists([&](const AstNodeVarRef* refp) { - // Cross reference is ambiguous - if (VN_IS(refp, VarXRef)) return true; + m_abort = nodep->exists([&](const AstVarRef* refp) { // Only care about reads if (refp->access().isWriteOnly()) return false; // Grab referenced variable - Variable* const tgtp = getTarget(refp); + AstVarScope* const vscp = refp->varScopep(); // Bail if not a compatible type - if (incompatible(tgtp)) return true; + if (incompatible(vscp)) return true; // Add to gen set, if not killed - assume whole variable read - if (m_currp->m_kill.count(tgtp)) return false; - m_currp->m_gen.emplace(tgtp); + if (m_currp->m_kill.count(vscp)) return false; + m_currp->m_gen.emplace(vscp); return false; }); } void updateKill(AstNode* nodep) { UASSERT_OBJ(!m_abort, nodep, "Doing useless work"); - m_abort = nodep->exists([&](const AstNodeVarRef* refp) { - // Cross reference is ambiguous - if (VN_IS(refp, VarXRef)) return true; + m_abort = nodep->exists([&](const AstVarRef* refp) { // Only care about writes if (refp->access().isReadOnly()) return false; // Grab referenced variable - Variable* const tgtp = getTarget(refp); + AstVarScope* const vscp = refp->varScopep(); // Bail if not a compatible type - if (incompatible(tgtp)) return true; + if (incompatible(vscp)) return true; // If whole written, add to kill set if (refp->nextp()) return false; if (VN_IS(refp->abovep(), Sel)) return false; - m_currp->m_kill.emplace(tgtp); + m_currp->m_kill.emplace(vscp); return false; }); } @@ -144,8 +119,8 @@ class CfgLiveVariables final : VNVisitorConst { BlockState& state = m_blockState[bb]; // liveIn = gen union (liveOut - kill) - std::unordered_set liveIn = state.m_gen; - for (Variable* const varp : state.m_liveOut) { + std::unordered_set liveIn = state.m_gen; + for (AstVarScope* const varp : state.m_liveOut) { if (state.m_kill.count(varp)) continue; liveIn.insert(varp); } @@ -219,25 +194,23 @@ class CfgLiveVariables final : VNVisitorConst { } public: - static std::unique_ptr> apply(const CfgGraph& cfg) { + static std::unique_ptr> apply(const CfgGraph& cfg) { CfgLiveVariables analysis{cfg}; // If failed, return nullptr if (analysis.m_abort) return nullptr; - // Gather variables live in to the entry blockstd::unique_ptr> - const std::unordered_set& lin = analysis.m_blockState[cfg.enter()].m_liveIn; - std::vector* const resultp = new std::vector{lin.begin(), lin.end()}; + // Gather variables live in to the entry blockstd::unique_ptr> + const std::unordered_set& lin = analysis.m_blockState[cfg.enter()].m_liveIn; + std::vector* const resultp + = new std::vector{lin.begin(), lin.end()}; // Sort for stability - std::stable_sort(resultp->begin(), resultp->end(), [](Variable* ap, Variable* bp) { // - return ap->name() < bp->name(); - }); - return std::unique_ptr>{resultp}; + std::stable_sort(resultp->begin(), resultp->end(), + [](AstVarScope* ap, AstVarScope* bp) { // + return ap->name() < bp->name(); + }); + return std::unique_ptr>{resultp}; } }; -std::unique_ptr> V3Cfg::liveVars(const CfgGraph& cfg) { - return CfgLiveVariables::apply(cfg); -} - std::unique_ptr> V3Cfg::liveVarScopes(const CfgGraph& cfg) { - return CfgLiveVariables::apply(cfg); + return CfgLiveVariables::apply(cfg); }