// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Implementations of simple passes over DfgGraph // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2025 by Wilson Snyder. This program is free software; you // can redistribute it and/or modify it under the terms of either the GNU // Lesser General Public License Version 3 or the Perl Artistic License // Version 2.0. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* #ifndef VERILATOR_V3DFGPATTERNSTATS_H_ #define VERILATOR_V3DFGPATTERNSTATS_H_ #include "V3Dfg.h" #include #include #include class V3DfgPatternStats final { static constexpr uint32_t MIN_PATTERN_DEPTH = 1; static constexpr uint32_t MAX_PATTERN_DEPTH = 4; std::map m_internedConsts; // Interned constants std::map m_internedVars; // Interned variables std::map m_internedSelLsbs; // Interned lsb value for selects std::map m_internedWordWidths; // Interned widths std::map m_internedWideWidths; // Interned widths std::map m_internedVertices; // Interned vertices // Maps from pattern to the number of times it appears, for each pattern depth std::vector> m_patterCounts{MAX_PATTERN_DEPTH + 1}; static std::string toLetters(size_t value, bool lowerCase = false) { const char base = lowerCase ? 'a' : 'A'; std::string s; do { s += static_cast(base + value % 26); } while (value /= 26); return s; } const std::string& internConst(const DfgConst& vtx) { const auto pair = m_internedConsts.emplace(vtx.num().ascii(false), "c"); if (pair.second) pair.first->second += toLetters(m_internedConsts.size() - 1); return pair.first->second; } const std::string& internVar(const DfgVertexVar& vtx) { const auto pair = m_internedVars.emplace(vtx.nodep(), "v"); if (pair.second) pair.first->second += toLetters(m_internedVars.size() - 1); return pair.first->second; } const std::string& internSelLsb(uint32_t value) { const auto pair = m_internedSelLsbs.emplace(value, ""); if (pair.second) pair.first->second += toLetters(m_internedSelLsbs.size() - 1); return pair.first->second; } const std::string& internWordWidth(uint32_t value) { const auto pair = m_internedWordWidths.emplace(value, ""); if (pair.second) pair.first->second += toLetters(m_internedWordWidths.size() - 1, true); return pair.first->second; } const std::string& internWideWidth(uint32_t value) { const auto pair = m_internedWideWidths.emplace(value, ""); if (pair.second) pair.first->second += toLetters(m_internedWideWidths.size() - 1); return pair.first->second; } const std::string& internVertex(const DfgVertex& vtx) { const auto pair = m_internedVertices.emplace(&vtx, "_"); if (pair.second) pair.first->second += toLetters(m_internedVertices.size() - 1); return pair.first->second; } // Render the vertx into ss, and return true if the recursion reached the given depth, // meaning an S-expression with that nesting level has been rendered. bool render(std::ostringstream& ss, const DfgVertex& vtx, uint32_t depth) { bool deep = depth == 0; if (const DfgConst* const constp = vtx.cast()) { // Base case 1: constant if (constp->isZero()) { ss << "'0"; } else if (constp->isOnes()) { ss << "'1"; } else { ss << internConst(*constp); } } else if (const DfgVertexVar* const varp = vtx.cast()) { // Base case 2: variable ss << internVar(*varp); } else if (depth == 0) { // Base case 3: deep vertex ss << internVertex(vtx); } else { // Recursively print an S-expression for the vertex // S-expression begin ss << '('; // Name ss << vtx.typeName(); // Specials if (const DfgSel* const selp = vtx.cast()) { ss << '@'; if (selp->lsb() == 0) { ss << '0'; } else { ss << internSelLsb(selp->lsb()); } } // Operands vtx.foreachSource([&](const DfgVertex& src) { ss << ' '; if (render(ss, src, depth - 1)) deep = true; return false; }); // S-expression end ss << ')'; // Mark it if it has multiple sinks if (vtx.hasMultipleSinks()) ss << '*'; } // Annotate type ss << ':'; if (!vtx.dtype().isPacked()) { vtx.dtype().astDtypep()->dumpSmall(ss); } else { const uint32_t width = vtx.size(); if (width == 1) { ss << '1'; } else if (width <= VL_QUADSIZE) { ss << internWordWidth(width); } else { ss << internWideWidth(width); } } // Done return deep; } public: V3DfgPatternStats() = default; void accumulate(const DfgGraph& dfg) { dfg.forEachVertex([&](const DfgVertex& vtx) { for (uint32_t i = MIN_PATTERN_DEPTH; i <= MAX_PATTERN_DEPTH; ++i) { std::ostringstream ss; if (render(ss, vtx, i)) m_patterCounts[i][ss.str()] += 1; m_internedConsts.clear(); m_internedVars.clear(); m_internedSelLsbs.clear(); m_internedWordWidths.clear(); m_internedWideWidths.clear(); m_internedVertices.clear(); } }); } void dump(const std::string& stage, std::ostream& os) { using Line = std::pair; for (uint32_t i = MIN_PATTERN_DEPTH; i <= MAX_PATTERN_DEPTH; ++i) { os << "DFG '" << stage << "' patterns with depth " << i << '\n'; // Pick up pattern accumulators with given depth const auto& patternCounts = m_patterCounts[i]; // Sort patterns, first by descending frequency, then lexically std::vector lines; lines.reserve(patternCounts.size()); for (const auto& pair : patternCounts) lines.emplace_back(pair); std::sort(lines.begin(), lines.end(), [](const Line& a, const Line& b) { if (a.second != b.second) return a.second > b.second; return a.first < b.first; }); // Print each pattern for (const auto& line : lines) { os << ' ' << std::setw(12) << std::right << line.second; os << ' ' << std::left << line.first << '\n'; } // Trailing new-line to separate sections os << '\n'; } } }; #endif