Narrow NBA shadow copies for partial writes (#8266)

Signed-off-by: Artur Bieniek <[email protected]>
This commit is contained in:
Artur Bieniek
2026-09-10 11:38:46 -04:00
committed by GitHub
parent ee792e235a
commit 09fd15af7b
3 changed files with 216 additions and 1 deletions
+44 -1
View File
@@ -56,6 +56,10 @@
//
// Constraint 3 should always hold with V3Delayed, will check assert it.
//
// If old-value reads prevent elimination, constant partial writes can still narrow both
// copies to the words containing those writes. Other shadow bits are never read. The
// assignments keep their original positions, so evaluation order and scheduling are unchanged.
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
@@ -124,6 +128,7 @@ class LifePostDlyVisitor final : public VNVisitorConst {
const AstExecGraph* m_execGraphp = nullptr; // Current AstExecGraph being processed (or null)
const ExecMTask* m_execMTaskp = nullptr; // Current ExecMTask being processed (or null)
VDouble0 m_statAssnDel; // Statistic tracking
VDouble0 m_statWordsSaved; // Words removed from NBA shadow copies
// Maps from Varscope to all their reads and writes
using LocMap = std::unordered_map<const AstVarScope*, std::vector<Location<AstVarRef>>>;
LocMap m_reads; // VarScope read locations
@@ -134,6 +139,40 @@ class LifePostDlyVisitor final : public VNVisitorConst {
bool m_inEvalNba = false; // Traversing under the 'nba' region entry point
// METHODS
void narrowCopies(AstNodeAssign* postp, AstVarScope* dVscp,
const std::vector<Location<AstVarRef>>& writes) {
if (!dVscp->isWide()) return;
AstNodeAssign* const prep = VN_AS(writes[0].nodep()->backp(), NodeAssign);
UASSERT_OBJ(VN_AS(prep->lhsp(), VarRef)->varScopep()
== VN_AS(postp->rhsp(), VarRef)->varScopep()
&& VN_AS(prep->rhsp(), VarRef)->varScopep()
== VN_AS(postp->lhsp(), VarRef)->varScopep(),
prep, "NBA shadow pre/post assignments are not reverse copies");
int lsb = dVscp->width();
int end = 0;
for (size_t i = 1; i < writes.size(); ++i) {
const AstSel* const selp = VN_CAST(writes[i].nodep()->backp(), Sel);
if (!selp || !VN_IS(selp->lsbp(), Const)) return;
const int start = selp->lsbConst();
if (start > dVscp->width() - selp->width()) return;
lsb = std::min(lsb, start);
end = std::max(end, start + selp->width());
}
// Copy one word-aligned range enclosing all writes, including any gaps.
lsb = VL_BITWORD_E(lsb) * VL_EDATASIZE;
end = std::min(VL_WORDS_I(end) * VL_EDATASIZE, dVscp->width());
const int width = end - lsb;
if (width == dVscp->width()) return;
for (AstNodeAssign* const assignp : {prep, postp}) {
FileLine* const flp = assignp->fileline();
assignp->lhsp(new AstSel{flp, assignp->lhsp()->unlinkFrBack(), lsb, width});
assignp->rhsp(new AstSel{flp, assignp->rhsp()->unlinkFrBack(), lsb, width});
assignp->dtypeFrom(assignp->lhsp());
}
m_statWordsSaved += 2 * (dVscp->widthWords() - VL_WORDS_I(width));
}
void squashAssignposts() {
for (const Location<AstNodeAssign>& assign : m_assigns) {
AstVarScope* const dVscp = VN_AS(assign.nodep()->rhsp(), VarRef)->varScopep();
@@ -172,7 +211,10 @@ class LifePostDlyVisitor final : public VNVisitorConst {
}
return true;
}();
if (!qRdOK) continue;
if (!qRdOK) {
narrowCopies(assign.nodep(), dVscp, dWrites);
continue;
}
}
// Mark variable for replacement
@@ -308,6 +350,7 @@ public:
}
~LifePostDlyVisitor() override {
V3Stats::addStat("Optimizations, Lifetime postassign deletions", m_statAssnDel);
V3Stats::addStat("Optimizations, Lifetime NBA copy words removed", m_statWordsSaved);
}
};