Internals: Simplify V3CfgLiveVariables to work only in scoped mode

No functional change
This commit is contained in:
Geza Lore 2026-06-08 14:45:49 +01:00
parent ece4d71e5b
commit 96336395d6
2 changed files with 33 additions and 62 deletions

View File

@ -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<std::vector<AstVar*>> 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<std::vector<AstVarScope*>> liveVarScopes(const CfgGraph&);
} //namespace V3Cfg

View File

@ -25,25 +25,19 @@
#include "V3Ast.h"
#include "V3Cfg.h"
#include <limits>
#include <memory>
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
template <bool T_Scoped>
class CfgLiveVariables final : VNVisitorConst {
// TYPES
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// State associted with each basic block
struct BlockState final {
// Variables used in block, before a complete assignment in the same block
std::unordered_set<Variable*> m_gen;
std::unordered_set<AstVarScope*> m_gen;
// Variables that are assigned a complete value in the basic block
std::unordered_set<Variable*> m_kill;
std::unordered_set<Variable*> m_liveIn; // Variables live on entry to the block
std::unordered_set<Variable*> m_liveOut; // Variables live on exit from the block
std::unordered_set<AstVarScope*> m_kill;
std::unordered_set<AstVarScope*> m_liveIn; // Variables live on entry to the block
std::unordered_set<AstVarScope*> 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<Variable*>(refp->varScopep());
} else {
return reinterpret_cast<Variable*>(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<AstVarScope*>(varp)->varp();
} else {
astVarp = reinterpret_cast<AstVar*>(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<Variable*> liveIn = state.m_gen;
for (Variable* const varp : state.m_liveOut) {
std::unordered_set<AstVarScope*> 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<std::vector<Variable*>> apply(const CfgGraph& cfg) {
static std::unique_ptr<std::vector<AstVarScope*>> 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<std::vector<Variable*>>
const std::unordered_set<Variable*>& lin = analysis.m_blockState[cfg.enter()].m_liveIn;
std::vector<Variable*>* const resultp = new std::vector<Variable*>{lin.begin(), lin.end()};
// Gather variables live in to the entry blockstd::unique_ptr<std::vector<AstVarScope*>>
const std::unordered_set<AstVarScope*>& lin = analysis.m_blockState[cfg.enter()].m_liveIn;
std::vector<AstVarScope*>* const resultp
= new std::vector<AstVarScope*>{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<std::vector<Variable*>>{resultp};
std::stable_sort(resultp->begin(), resultp->end(),
[](AstVarScope* ap, AstVarScope* bp) { //
return ap->name() < bp->name();
});
return std::unique_ptr<std::vector<AstVarScope*>>{resultp};
}
};
std::unique_ptr<std::vector<AstVar*>> V3Cfg::liveVars(const CfgGraph& cfg) {
return CfgLiveVariables</* T_Scoped: */ false>::apply(cfg);
}
std::unique_ptr<std::vector<AstVarScope*>> V3Cfg::liveVarScopes(const CfgGraph& cfg) {
return CfgLiveVariables</* T_Scoped: */ true>::apply(cfg);
return CfgLiveVariables::apply(cfg);
}