Optimize expanded constant pool words (#6979)
Re-inline ConstPool entries in V3Subst that have been expanded into word-wise accessed by V3Expand. This enables downstream constant folding on the word-wise expressions. As V3Subst now understands ConstPool entries, we can also omit expanding straight assignments with a ConstPool entry on the RHS. This allows the C++ compiler to see the memcpy directly.
This commit is contained in:
parent
7ca113a84f
commit
d3f608058f
|
|
@ -71,7 +71,10 @@ public:
|
|||
class ExpandVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstNode::user1() -> bool. Processed
|
||||
// AstNode::user2() -> See ExpandOkVisitor
|
||||
// AstVar::user3() -> bool. Is a constant pool variable
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser3InUse m_inuser3;
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
AstNode* m_stmtp = nullptr; // Current statement
|
||||
|
|
@ -313,6 +316,9 @@ class ExpandVisitor final : public VNVisitor {
|
|||
//-------- Uniops
|
||||
bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) {
|
||||
UINFO(8, " Wordize ASSIGN(VARREF) " << nodep);
|
||||
// Special case: do not expand assignment of constant pool variables.
|
||||
// V3Subst undestands these directly.
|
||||
if (rhsp->varp()->user3()) return false;
|
||||
if (!doExpandWide(nodep)) return false;
|
||||
for (int w = 0; w < nodep->widthWords(); ++w) {
|
||||
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
|
||||
|
|
@ -429,6 +435,15 @@ class ExpandVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// Mark constant pool variables
|
||||
for (AstNode* np = nodep->constPoolp()->modp()->stmtsp(); np; np = np->nextp()) {
|
||||
if (VN_IS(np, Var)) np->user3(true);
|
||||
}
|
||||
|
||||
iterateAndNextNull(nodep->modulesp());
|
||||
}
|
||||
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_funcp);
|
||||
VL_RESTORER(m_nTmps);
|
||||
|
|
|
|||
|
|
@ -218,6 +218,8 @@ AstNodeExpr* SubstVarEntry::substRecord(SubstVarEntry::Record& record) {
|
|||
class SubstVisitor final : public VNVisitor {
|
||||
// NODE STATE - only Under AstCFunc
|
||||
// AstVar::user1p -> SubstVarEntry* for assignment tracking. Also used by SubstValidVisitor
|
||||
// AstVar::user2 -> bool. Is a constant pool variable
|
||||
const VNUser2InUse m_user2InUse;
|
||||
|
||||
// STATE
|
||||
std::deque<SubstVarEntry> m_entries; // Storage for SubstVarEntry instances
|
||||
|
|
@ -230,6 +232,7 @@ class SubstVisitor final : public VNVisitor {
|
|||
size_t m_nWholeSubstituted = 0; // Number of whole variables substituted
|
||||
size_t m_nWordAssignDeleted = 0; // Number of word assignments deleted
|
||||
size_t m_nWholeAssignDeleted = 0; // Number of whole variable assignments deleted
|
||||
size_t m_nConstWordsReinlined = 0; // Number of constant words substituted
|
||||
|
||||
static constexpr uint32_t SUBST_MAX_OPS_SUBST = 30; // Maximum number of ops to substitute in
|
||||
static constexpr uint32_t SUBST_MAX_OPS_NA = 9999; // Not allowed to substitute
|
||||
|
|
@ -260,7 +263,7 @@ class SubstVisitor final : public VNVisitor {
|
|||
bool isSubstitutable(AstVar* nodep) { return nodep->isStatementTemp() && !nodep->noSubst(); }
|
||||
|
||||
void substitute(AstNode* nodep, AstNodeExpr* substp) {
|
||||
AstNodeExpr* newp = substp->cloneTreePure(true);
|
||||
AstNodeExpr* newp = substp->backp() ? substp->cloneTreePure(true) : substp;
|
||||
if (!nodep->isQuad() && newp->isQuad()) {
|
||||
newp = new AstCCast{newp->fileline(), newp, nodep};
|
||||
}
|
||||
|
|
@ -270,6 +273,16 @@ class SubstVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
// VISITORS
|
||||
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// Mark constant pool variables
|
||||
for (AstNode* np = nodep->constPoolp()->modp()->stmtsp(); np; np = np->nextp()) {
|
||||
if (VN_IS(np, Var)) np->user2(true);
|
||||
}
|
||||
|
||||
iterateAndNextNull(nodep->modulesp());
|
||||
}
|
||||
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_funcp, nodep, "Should not nest");
|
||||
UASSERT_OBJ(m_entries.empty(), nodep, "Should not visit outside functions");
|
||||
|
|
@ -357,8 +370,21 @@ class SubstVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
|
||||
// Otherwise it's a read, substitute it if possible
|
||||
// Otherwise it's a read,
|
||||
UASSERT_OBJ(refp->access().isReadOnly(), nodep, "Invalid access");
|
||||
|
||||
// If it's a constant pool variable, substiute with the constant word
|
||||
AstVar* const varp = refp->varp();
|
||||
if (varp->user2()) {
|
||||
AstConst* const constp = VN_AS(varp->valuep(), Const);
|
||||
const uint32_t value = constp->num().edataWord(word);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
++m_nConstWordsReinlined;
|
||||
substitute(nodep, new AstConst{flp, AstConst::SizedEData{}, value});
|
||||
return;
|
||||
}
|
||||
|
||||
// Substitute other variables if possible
|
||||
if (isSubstitutable(refp->varp())) {
|
||||
if (AstNodeExpr* const substp = entry.substWord(word)) {
|
||||
++m_nWordSubstituted;
|
||||
|
|
@ -425,14 +451,15 @@ class SubstVisitor final : public VNVisitor {
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit SubstVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
explicit SubstVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~SubstVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Substituted temps", m_nSubst);
|
||||
V3Stats::addStat("Optimizations, Whole variable assignments deleted",
|
||||
V3Stats::addStat("Optimizations, Subst, Substituted temps", m_nSubst);
|
||||
V3Stats::addStat("Optimizations, Subst, Constant words reinlined", m_nConstWordsReinlined);
|
||||
V3Stats::addStat("Optimizations, Subst, Whole variable assignments deleted",
|
||||
m_nWholeAssignDeleted);
|
||||
V3Stats::addStat("Optimizations, Whole variables substituted", m_nWholeSubstituted);
|
||||
V3Stats::addStat("Optimizations, Word assignments deleted", m_nWordAssignDeleted);
|
||||
V3Stats::addStat("Optimizations, Words substituted", m_nWordSubstituted);
|
||||
V3Stats::addStat("Optimizations, Subst, Whole variables substituted", m_nWholeSubstituted);
|
||||
V3Stats::addStat("Optimizations, Subst, Word assignments deleted", m_nWordAssignDeleted);
|
||||
V3Stats::addStat("Optimizations, Subst, Words substituted", m_nWordSubstituted);
|
||||
UASSERT(m_entries.empty(), "Should not visit outside functions");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@ test.scenarios('simulator_st')
|
|||
test.compile(verilator_flags2=["--stats"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Substituted temps\s+(\d+)', 43)
|
||||
test.file_grep(test.stats, r'Optimizations, Subst, Constant words reinlined\s+(\d+)', 156)
|
||||
test.file_grep(test.stats, r'Optimizations, Subst, Substituted temps\s+(\d+)', 225)
|
||||
test.file_grep(test.stats, r'Optimizations, Subst, Whole variable assignments deleted\s+(\d+)',
|
||||
1)
|
||||
test.file_grep(test.stats, r'Optimizations, Subst, Whole variables substituted\s+(\d+)', 1)
|
||||
test.file_grep(test.stats, r'Optimizations, Subst, Word assignments deleted\s+(\d+)', 68)
|
||||
test.file_grep(test.stats, r'Optimizations, Subst, Words substituted\s+(\d+)', 68)
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module t (
|
|||
|
||||
integer i;
|
||||
reg [94:0] w95;
|
||||
reg [399:0] w400;
|
||||
|
||||
integer cyc = 0;
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ module t (
|
|||
if (cyc == 0) begin
|
||||
// Setup
|
||||
w95 = {95{1'b1}};
|
||||
w400 = '1;
|
||||
end
|
||||
else if (cyc == 1) begin
|
||||
if (w95++ != {95{1'b1}}) $stop;
|
||||
|
|
@ -34,6 +36,15 @@ module t (
|
|||
if (w95 != {95{1'b0}}) $stop;
|
||||
if (--w95 != {95{1'b1}}) $stop;
|
||||
if (w95 != {95{1'b1}}) $stop;
|
||||
|
||||
if (w400++ != {400{1'b1}}) $stop;
|
||||
if (w400 != {400{1'b0}}) $stop;
|
||||
if (w400-- != {400{1'b0}}) $stop;
|
||||
if (w400 != {400{1'b1}}) $stop;
|
||||
if (++w400 != {400{1'b0}}) $stop;
|
||||
if (w400 != {400{1'b0}}) $stop;
|
||||
if (--w400 != {400{1'b1}}) $stop;
|
||||
if (w400 != {400{1'b1}}) $stop;
|
||||
end
|
||||
else if (cyc == 99) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ test.top_filename = "t/t_opt_life.v"
|
|||
test.compile(verilator_flags2=['--stats', '-fno-subst', '-fno-subst-const'])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep_not(test.stats, r'Optimizations, Substituted temps\s+(\d+)')
|
||||
test.file_grep_not(test.stats, r'Optimizations, Subst,')
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ test.compile(verilator_flags2=[
|
|||
test.execute()
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Reloop iterations\s+(\d+)', 768)
|
||||
test.file_grep(test.stats, r'Optimizations, Reloops\s+(\d+)', 3)
|
||||
test.file_grep(test.stats, r'Optimizations, Reloop iterations\s+(\d+)', 512)
|
||||
test.file_grep(test.stats, r'Optimizations, Reloops\s+(\d+)', 2)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
Loading…
Reference in New Issue