2025-07-10 19:46:45 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Converting cyclic DFGs into acyclic DFGs
|
|
|
|
|
//
|
|
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
|
|
|
|
#include "V3Dfg.h"
|
|
|
|
|
#include "V3DfgPasses.h"
|
|
|
|
|
#include "V3Hash.h"
|
|
|
|
|
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
#include <algorithm>
|
2025-07-11 20:19:09 +02:00
|
|
|
#include <deque>
|
2025-07-10 19:46:45 +02:00
|
|
|
#include <fstream>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
class TraceDriver final : public DfgVisitor {
|
|
|
|
|
// TYPES
|
|
|
|
|
|
|
|
|
|
// Structure denoting currently visited vertex with the MSB and LSB we are searching for
|
|
|
|
|
struct Visited final {
|
|
|
|
|
DfgVertex* m_vtxp;
|
|
|
|
|
uint32_t m_lsb;
|
|
|
|
|
uint32_t m_msb;
|
|
|
|
|
|
|
|
|
|
Visited() = delete;
|
|
|
|
|
Visited(DfgVertex* vtxp, uint32_t lsb, uint32_t msb)
|
|
|
|
|
: m_vtxp{vtxp}
|
|
|
|
|
, m_lsb{lsb}
|
|
|
|
|
, m_msb{msb} {}
|
|
|
|
|
|
|
|
|
|
struct Hash final {
|
|
|
|
|
size_t operator()(const Visited& item) const {
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress unreadVariable
|
|
|
|
|
V3Hash hash{item.m_vtxp};
|
2025-07-10 19:46:45 +02:00
|
|
|
hash += item.m_lsb;
|
|
|
|
|
hash += item.m_msb;
|
|
|
|
|
return hash.value();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Equal final {
|
|
|
|
|
bool operator()(const Visited& a, const Visited& b) const {
|
2025-08-05 15:47:51 +02:00
|
|
|
return a.m_vtxp == b.m_vtxp && a.m_lsb == b.m_lsb && a.m_msb == b.m_msb;
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
DfgGraph& m_dfg; // The graph being processed
|
|
|
|
|
// The strongly connected component we are trying to escape
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t m_component;
|
2025-07-11 20:19:09 +02:00
|
|
|
const bool m_aggressive; // Trace aggressively, creating intermediate ops
|
2025-07-10 19:46:45 +02:00
|
|
|
uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex
|
|
|
|
|
uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex
|
2025-09-02 17:50:40 +02:00
|
|
|
DfgVertex* m_defaultp = nullptr; // When tracing a variable, this is its 'defaultp', if any
|
2025-07-10 19:46:45 +02:00
|
|
|
// Result of tracing the currently visited Vertex. Use SET_RESULT below!
|
|
|
|
|
DfgVertex* m_resp = nullptr;
|
|
|
|
|
std::vector<DfgVertex*> m_newVtxps; // New vertices created during the traversal
|
|
|
|
|
|
|
|
|
|
std::vector<Visited> m_stack; // Stack of currently visited vertices
|
|
|
|
|
// Denotes if a 'Visited' entry appear in m_stack
|
|
|
|
|
std::unordered_map<Visited, bool, Visited::Hash, Visited::Equal> m_visited;
|
|
|
|
|
|
2025-08-10 19:14:02 +02:00
|
|
|
#ifdef VL_DEBUG
|
2025-07-11 20:19:09 +02:00
|
|
|
std::ofstream m_lineCoverageFile; // Line coverage file, just for testing
|
2025-08-10 19:14:02 +02:00
|
|
|
#endif
|
2025-07-11 20:19:09 +02:00
|
|
|
|
2025-07-10 19:46:45 +02:00
|
|
|
// METHODS
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
// Create and return a new Vertex and add it to m_newVtxps. Fileline is
|
|
|
|
|
// taken from 'refp', but 'refp' is otherwise not used. You should
|
2025-07-10 19:46:45 +02:00
|
|
|
// always use this to create new vertices, so unused ones (if a trace
|
2025-07-24 16:31:09 +02:00
|
|
|
// eventually fails) can be cleaned up at the end. This also sets the
|
2025-08-05 14:03:30 +02:00
|
|
|
// vertex user<uint64_t> to 0, indicating the new vertex is not part of a
|
2025-07-24 16:31:09 +02:00
|
|
|
// strongly connected component. This should always be true, as all the
|
|
|
|
|
// vertices we create here are driven from outside the component we are
|
|
|
|
|
// trying to escape, and will sink into that component. Given those are
|
|
|
|
|
// separate SCCs, these new vertices must be acyclic.
|
2025-07-10 19:46:45 +02:00
|
|
|
template <typename Vertex>
|
2025-07-11 20:19:09 +02:00
|
|
|
Vertex* make(const DfgVertex* refp, uint32_t width) {
|
2025-07-10 19:46:45 +02:00
|
|
|
static_assert(std::is_base_of<DfgVertex, Vertex>::value //
|
2025-07-11 20:19:09 +02:00
|
|
|
&& !std::is_base_of<DfgVertexVar, Vertex>::value,
|
|
|
|
|
"Should only make operation vertices and constants");
|
|
|
|
|
|
|
|
|
|
constexpr bool okWithoutAggressive = //
|
|
|
|
|
std::is_same<DfgConst, Vertex>::value //
|
|
|
|
|
|| std::is_same<DfgSel, Vertex>::value //
|
|
|
|
|
|| std::is_same<DfgConcat, Vertex>::value //
|
|
|
|
|
|| std::is_same<DfgExtend, Vertex>::value;
|
|
|
|
|
|
|
|
|
|
UASSERT_OBJ(
|
|
|
|
|
okWithoutAggressive || m_aggressive, refp,
|
|
|
|
|
"Should only create Const, Sel, Concat, Exend Vertices without aggressive mode");
|
|
|
|
|
|
|
|
|
|
if VL_CONSTEXPR_CXX17 (std::is_same<DfgConst, Vertex>::value) {
|
2025-09-02 17:50:40 +02:00
|
|
|
DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width, 0};
|
2025-08-05 14:03:30 +02:00
|
|
|
vtxp->template setUser<uint64_t>(0);
|
2025-07-11 20:19:09 +02:00
|
|
|
m_newVtxps.emplace_back(vtxp);
|
|
|
|
|
return reinterpret_cast<Vertex*>(vtxp);
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: this is a gross hack around lack of C++17 'if constexpr' Vtx is always Vertex
|
|
|
|
|
// when this code is actually executed, but needs a fudged type to type check when
|
|
|
|
|
// Vertex is DfgConst, in which case this code is unreachable ...
|
|
|
|
|
using Vtx = typename std::conditional<std::is_same<DfgConst, Vertex>::value, DfgSel,
|
|
|
|
|
Vertex>::type;
|
2025-09-02 17:50:40 +02:00
|
|
|
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
2025-07-11 20:19:09 +02:00
|
|
|
Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep};
|
2025-08-05 14:03:30 +02:00
|
|
|
vtxp->template setUser<uint64_t>(0);
|
2025-07-11 20:19:09 +02:00
|
|
|
m_newVtxps.emplace_back(vtxp);
|
|
|
|
|
return reinterpret_cast<Vertex*>(vtxp);
|
|
|
|
|
}
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Continue tracing drivers of the given vertex, at the given LSB. Every
|
|
|
|
|
// visitor should call this to continue the traversal, then immediately
|
|
|
|
|
// return after the call. 'visit' methods should not call 'iterate', call
|
|
|
|
|
// this method instead, which checks for cycles.
|
|
|
|
|
DfgVertex* trace(DfgVertex* const vtxp, const uint32_t msb, const uint32_t lsb) {
|
|
|
|
|
UASSERT_OBJ(!vtxp->is<DfgVarArray>(), vtxp, "Cannot trace array variables");
|
|
|
|
|
UASSERT_OBJ(vtxp->width() > msb, vtxp, "Traced Vertex too narrow");
|
|
|
|
|
|
|
|
|
|
// Push to stack
|
|
|
|
|
m_stack.emplace_back(vtxp, msb, lsb);
|
|
|
|
|
bool& onStackr = m_visited[m_stack.back()];
|
|
|
|
|
|
|
|
|
|
// Check for true combinational cycles
|
|
|
|
|
if (onStackr) {
|
|
|
|
|
// Pop from stack
|
|
|
|
|
m_stack.pop_back();
|
|
|
|
|
|
|
|
|
|
// Note: could issue a "proper combinational cycle" error here,
|
|
|
|
|
// but constructing a legible error message is hard as the Vertex
|
|
|
|
|
// Filelines can be very rough after optimizations (could consider
|
|
|
|
|
// reporting only the variables involved). Also this pass might
|
|
|
|
|
// run mulitple times and report the same error again. There will
|
|
|
|
|
// be an UNOPTFLAT issued during scheduling anyway, and the true
|
|
|
|
|
// cycle might still settle at run-time.
|
|
|
|
|
|
|
|
|
|
// Stop trace
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trace the vertex
|
|
|
|
|
onStackr = true;
|
|
|
|
|
|
2025-09-02 17:50:40 +02:00
|
|
|
// If the currently traced vertex is in a different component, then we
|
|
|
|
|
// found what we were looking for. However, keep going past a splice,
|
|
|
|
|
// or a unit as they cannot be use directly (they must always feed into
|
|
|
|
|
// a variable, so we can't make them drive arbitrary logic)
|
|
|
|
|
if (vtxp->getUser<uint64_t>() != m_component //
|
|
|
|
|
&& !vtxp->is<DfgVertexSplice>() //
|
|
|
|
|
&& !vtxp->is<DfgUnitArray>()) {
|
2025-07-10 19:46:45 +02:00
|
|
|
if (msb != vtxp->width() - 1 || lsb != 0) {
|
|
|
|
|
// Apply a Sel to extract the relevant bits if only a part is needed
|
2025-07-11 20:19:09 +02:00
|
|
|
DfgSel* const selp = make<DfgSel>(vtxp, msb - lsb + 1);
|
2025-07-10 19:46:45 +02:00
|
|
|
selp->fromp(vtxp);
|
|
|
|
|
selp->lsb(lsb);
|
|
|
|
|
m_resp = selp;
|
|
|
|
|
} else {
|
|
|
|
|
// Otherwise just return the vertex
|
|
|
|
|
m_resp = vtxp;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Otherwise visit the vertex
|
|
|
|
|
VL_RESTORER(m_msb);
|
|
|
|
|
VL_RESTORER(m_lsb);
|
|
|
|
|
m_msb = msb;
|
|
|
|
|
m_lsb = lsb;
|
|
|
|
|
m_resp = nullptr;
|
|
|
|
|
iterate(vtxp);
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(!m_resp || m_resp->width() == (msb - lsb + 1), vtxp, "Wrong result width");
|
|
|
|
|
|
|
|
|
|
// Pop from stack
|
|
|
|
|
onStackr = false;
|
|
|
|
|
m_stack.pop_back();
|
|
|
|
|
|
|
|
|
|
// Done
|
2025-07-11 20:19:09 +02:00
|
|
|
if (!m_resp) {
|
|
|
|
|
UINFO(9, "TraceDriver - Failed to trace vertex of type: " << vtxp->typeName());
|
|
|
|
|
}
|
2025-07-10 19:46:45 +02:00
|
|
|
return m_resp;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
template <typename Vertex>
|
2025-08-15 09:20:36 +02:00
|
|
|
Vertex* traceBitwiseBinary(Vertex* vtxp) {
|
2025-07-11 20:19:09 +02:00
|
|
|
static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value,
|
|
|
|
|
"Should only call on DfgVertexBinary");
|
|
|
|
|
if (DfgVertex* const rp = trace(vtxp->rhsp(), m_msb, m_lsb)) {
|
|
|
|
|
if (DfgVertex* const lp = trace(vtxp->lhsp(), m_msb, m_lsb)) {
|
|
|
|
|
Vertex* const resp = make<Vertex>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->rhsp(rp);
|
|
|
|
|
resp->lhsp(lp);
|
|
|
|
|
return resp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 09:20:36 +02:00
|
|
|
template <typename Vertex>
|
|
|
|
|
DfgVertex* traceAddSub(Vertex* vtxp) {
|
|
|
|
|
static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value,
|
|
|
|
|
"Should only call on DfgVertexBinary");
|
|
|
|
|
if (DfgVertex* const rp = trace(vtxp->rhsp(), m_msb, 0)) {
|
|
|
|
|
if (DfgVertex* const lp = trace(vtxp->lhsp(), m_msb, 0)) {
|
|
|
|
|
Vertex* const opp = make<Vertex>(vtxp, m_msb + 1);
|
|
|
|
|
opp->rhsp(rp);
|
|
|
|
|
opp->lhsp(lp);
|
|
|
|
|
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
selp->fromp(opp);
|
|
|
|
|
selp->lsb(m_lsb);
|
|
|
|
|
return selp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Vertex>
|
|
|
|
|
Vertex* traceCmp(Vertex* vtxp) {
|
|
|
|
|
static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value,
|
|
|
|
|
"Should only call on DfgVertexBinary");
|
|
|
|
|
if (DfgVertex* const rp = trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0)) {
|
|
|
|
|
if (DfgVertex* const lp = trace(vtxp->lhsp(), vtxp->lhsp()->width() - 1, 0)) {
|
|
|
|
|
Vertex* const resp = make<Vertex>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->rhsp(rp);
|
|
|
|
|
resp->lhsp(lp);
|
|
|
|
|
return resp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
// Predicate to do determine if vtxp[$bits(vtxp)-1:idx] is known to be zero
|
|
|
|
|
// Somewhat rudimentary but sufficient for current purposes.
|
|
|
|
|
static bool knownToBeZeroAtAndAbove(const DfgVertex* vtxp, uint32_t idx) {
|
|
|
|
|
if (const DfgConcat* const catp = vtxp->cast<DfgConcat>()) {
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgConst* const lConstp = catp->lhsp()->cast<DfgConst>();
|
2025-07-11 20:19:09 +02:00
|
|
|
return lConstp && idx >= catp->rhsp()->width() && lConstp->isZero();
|
|
|
|
|
}
|
|
|
|
|
if (const DfgExtend* const extp = vtxp->cast<DfgExtend>()) {
|
|
|
|
|
return idx >= extp->srcp()->width();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Like knownToBeZeroAtAndAbove, but checks vtxp[idx:0]
|
|
|
|
|
static bool knownToBeZeroAtAndBelow(const DfgVertex* vtxp, uint32_t idx) {
|
|
|
|
|
if (const DfgConcat* const catp = vtxp->cast<DfgConcat>()) {
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgConst* const rConstp = catp->rhsp()->cast<DfgConst>();
|
2025-07-11 20:19:09 +02:00
|
|
|
return rConstp && idx < rConstp->width() && rConstp->isZero();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-10 19:46:45 +02:00
|
|
|
// Use this macro to set the result in 'visit' methods. This also emits
|
|
|
|
|
// a line to m_lineCoverageFile for testing.
|
|
|
|
|
// TODO: Use C++20 std::source_location instead of a macro
|
2025-08-10 19:14:02 +02:00
|
|
|
#ifdef VL_DEBUG
|
2025-07-10 19:46:45 +02:00
|
|
|
#define SET_RESULT(vtxp) \
|
|
|
|
|
do { \
|
|
|
|
|
m_resp = vtxp; \
|
|
|
|
|
if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \
|
|
|
|
|
} while (false)
|
2025-08-10 19:14:02 +02:00
|
|
|
#else
|
|
|
|
|
#define SET_RESULT(vtxp) m_resp = vtxp;
|
|
|
|
|
#endif
|
2025-07-10 19:46:45 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
void visit(DfgVertex* vtxp) override {
|
|
|
|
|
// Base case: cannot continue ...
|
|
|
|
|
UINFO(9, "TraceDriver - Unhandled vertex type: " << vtxp->typeName());
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
void visit(DfgSplicePacked* vtxp) override {
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
struct Driver final {
|
|
|
|
|
DfgVertex* m_vtxp;
|
|
|
|
|
uint32_t m_lsb; // LSB of driven range (internal, not Verilog)
|
|
|
|
|
uint32_t m_msb; // MSB of driven range (internal, not Verilog)
|
|
|
|
|
Driver() = delete;
|
|
|
|
|
Driver(DfgVertex* vtxp, uint32_t lsb, uint32_t msb)
|
|
|
|
|
: m_vtxp{vtxp}
|
|
|
|
|
, m_lsb{lsb}
|
|
|
|
|
, m_msb{msb} {}
|
|
|
|
|
};
|
|
|
|
|
std::vector<Driver> drivers;
|
|
|
|
|
|
|
|
|
|
// Look at all the drivers, one might cover the whole range, but also gathe all drivers
|
2025-09-02 17:50:40 +02:00
|
|
|
bool tryWholeDefault = m_defaultp;
|
|
|
|
|
const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
|
|
|
|
|
const uint32_t msb = lsb + src.width() - 1;
|
|
|
|
|
drivers.emplace_back(&src, lsb, msb);
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
// Check if this driver covers any of the bits, then we can't use whole default
|
|
|
|
|
if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false;
|
|
|
|
|
// If it does not cover the whole searched bit range, move on
|
2025-09-02 17:50:40 +02:00
|
|
|
if (m_lsb < lsb || msb < m_msb) return false;
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
// Driver covers whole search range, trace that and we are done
|
2025-09-02 17:50:40 +02:00
|
|
|
SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
if (done) return;
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
|
|
|
|
|
// Trace the default driver if no other drivers cover the searched range
|
2025-09-02 17:50:40 +02:00
|
|
|
if (tryWholeDefault) {
|
|
|
|
|
SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hard case: We need to combine multiple drivers to produce the searched bit range
|
|
|
|
|
|
|
|
|
|
// Sort ragnes (they are non-overlapping)
|
|
|
|
|
std::sort(drivers.begin(), drivers.end(),
|
|
|
|
|
[](const Driver& a, const Driver& b) { return a.m_lsb < b.m_lsb; });
|
|
|
|
|
|
|
|
|
|
// Gather terms
|
|
|
|
|
std::vector<DfgVertex*> termps;
|
|
|
|
|
for (const Driver& driver : drivers) {
|
|
|
|
|
// Driver is below the searched LSB, move on
|
|
|
|
|
if (m_lsb > driver.m_msb) continue;
|
|
|
|
|
// Driver is above the searched MSB, done
|
|
|
|
|
if (driver.m_lsb > m_msb) break;
|
|
|
|
|
// Gap below this driver, trace default to fill it
|
|
|
|
|
if (driver.m_lsb > m_lsb) {
|
2025-09-02 17:50:40 +02:00
|
|
|
if (!m_defaultp) return;
|
|
|
|
|
DfgVertex* const termp = trace(m_defaultp, driver.m_lsb - 1, m_lsb);
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (!termp) return;
|
|
|
|
|
termps.emplace_back(termp);
|
|
|
|
|
m_lsb = driver.m_lsb;
|
|
|
|
|
}
|
|
|
|
|
// Driver covers searched range, pick the needed/available bits
|
|
|
|
|
uint32_t lim = std::min(m_msb, driver.m_msb);
|
|
|
|
|
DfgVertex* const termp
|
|
|
|
|
= trace(driver.m_vtxp, lim - driver.m_lsb, m_lsb - driver.m_lsb);
|
|
|
|
|
if (!termp) return;
|
|
|
|
|
termps.emplace_back(termp);
|
|
|
|
|
m_lsb = lim + 1;
|
|
|
|
|
}
|
|
|
|
|
if (m_msb >= m_lsb) {
|
2025-09-02 17:50:40 +02:00
|
|
|
if (!m_defaultp) return;
|
|
|
|
|
DfgVertex* const termp = trace(m_defaultp, m_msb, m_lsb);
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (!termp) return;
|
|
|
|
|
termps.emplace_back(termp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The earlier cheks cover the case when either a whole driver or the default covers
|
|
|
|
|
// the whole range, so there should be at least 2 terms required here.
|
|
|
|
|
UASSERT_OBJ(termps.size() >= 2, vtxp, "Should have returned in special cases");
|
|
|
|
|
|
|
|
|
|
// Concatenate all terms and set result
|
|
|
|
|
DfgVertex* resp = termps.front();
|
|
|
|
|
for (size_t i = 1; i < termps.size(); ++i) {
|
|
|
|
|
DfgVertex* const termp = termps[i];
|
|
|
|
|
DfgConcat* const catp = make<DfgConcat>(termp, resp->width() + termp->width());
|
|
|
|
|
catp->rhsp(resp);
|
|
|
|
|
catp->lhsp(termp);
|
|
|
|
|
resp = catp;
|
|
|
|
|
}
|
|
|
|
|
SET_RESULT(resp);
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
void visit(DfgVarPacked* vtxp) override {
|
2025-09-02 17:50:40 +02:00
|
|
|
VL_RESTORER(m_defaultp);
|
|
|
|
|
m_defaultp = vtxp->defaultp();
|
2025-07-14 23:09:34 +02:00
|
|
|
if (DfgVertex* const srcp = vtxp->srcp()) {
|
|
|
|
|
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
void visit(DfgArraySel* vtxp) override {
|
|
|
|
|
// Only constant select
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>();
|
2025-07-22 09:23:45 +02:00
|
|
|
if (!idxp) return;
|
|
|
|
|
// From a variable
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgVarArray* varp = vtxp->fromp()->cast<DfgVarArray>();
|
2025-07-22 09:23:45 +02:00
|
|
|
if (!varp) return;
|
|
|
|
|
// Skip through intermediate variables
|
|
|
|
|
while (varp->srcp() && varp->srcp()->is<DfgVarArray>()) {
|
|
|
|
|
varp = varp->srcp()->as<DfgVarArray>();
|
|
|
|
|
}
|
|
|
|
|
// Find driver
|
|
|
|
|
if (!varp->srcp()) return;
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
|
|
|
|
|
// Driver might be a splice
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const DfgSpliceArray* const splicep = varp->srcp()->cast<DfgSpliceArray>()) {
|
|
|
|
|
const DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT());
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (!driverp) return;
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>();
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (!uap) return;
|
|
|
|
|
// Trace the driver
|
|
|
|
|
SET_RESULT(trace(uap->srcp(), m_msb, m_lsb));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Or a unit array
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgUnitArray* const uap = varp->srcp()->cast<DfgUnitArray>();
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (!uap) return;
|
2025-07-22 09:23:45 +02:00
|
|
|
// Trace the driver
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
UASSERT_OBJ(idxp->toSizeT() == 0, vtxp, "Array Index out of range");
|
|
|
|
|
SET_RESULT(trace(uap->srcp(), m_msb, m_lsb));
|
2025-07-22 09:23:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-10 19:46:45 +02:00
|
|
|
void visit(DfgConcat* vtxp) override {
|
|
|
|
|
DfgVertex* const rhsp = vtxp->rhsp();
|
|
|
|
|
DfgVertex* const lhsp = vtxp->lhsp();
|
|
|
|
|
const uint32_t rWidth = rhsp->width();
|
|
|
|
|
// If the traced bits are wholly in the RHS
|
|
|
|
|
if (rWidth > m_msb) {
|
|
|
|
|
SET_RESULT(trace(rhsp, m_msb, m_lsb));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If the traced bits are wholly in the LHS
|
|
|
|
|
if (m_lsb >= rWidth) {
|
|
|
|
|
SET_RESULT(trace(lhsp, m_msb - rWidth, m_lsb - rWidth));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
// The traced bit spans both sides, attempt to trace both
|
2025-07-10 19:46:45 +02:00
|
|
|
if (DfgVertex* const rp = trace(rhsp, rWidth - 1, m_lsb)) {
|
|
|
|
|
if (DfgVertex* const lp = trace(lhsp, m_msb - rWidth, 0)) {
|
2025-07-11 20:19:09 +02:00
|
|
|
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
|
2025-07-10 19:46:45 +02:00
|
|
|
resp->rhsp(rp);
|
|
|
|
|
resp->lhsp(lp);
|
|
|
|
|
SET_RESULT(resp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgExtend* vtxp) override {
|
|
|
|
|
DfgVertex* const srcp = vtxp->srcp();
|
2025-07-11 20:19:09 +02:00
|
|
|
const uint32_t sWidth = srcp->width();
|
|
|
|
|
// If the traced bits are wholly in the input
|
|
|
|
|
if (sWidth > m_msb) {
|
2025-07-10 19:46:45 +02:00
|
|
|
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
// If the traced bits are wholly in the extension
|
|
|
|
|
if (m_lsb >= sWidth) {
|
|
|
|
|
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// The traced bits span both sides
|
|
|
|
|
if (DfgVertex* const sp = trace(srcp, sWidth - 1, m_lsb)) {
|
|
|
|
|
DfgExtend* const resp = make<DfgExtend>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->srcp(sp);
|
|
|
|
|
SET_RESULT(resp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgSel* vtxp) override {
|
|
|
|
|
const uint32_t lsb = vtxp->lsb();
|
|
|
|
|
SET_RESULT(trace(vtxp->srcp(), m_msb + lsb, m_lsb + lsb));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
void visit(DfgNot* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
|
|
|
|
if (DfgVertex* const sp = trace(vtxp->srcp(), m_msb, m_lsb)) {
|
|
|
|
|
DfgNot* const resp = make<DfgNot>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->srcp(sp);
|
|
|
|
|
SET_RESULT(resp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgAnd* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceBitwiseBinary(vtxp));
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgOr* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceBitwiseBinary(vtxp));
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgXor* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceBitwiseBinary(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgAdd* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceAddSub(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgSub* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceAddSub(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgEq* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceCmp(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgNeq* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceCmp(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgLt* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceCmp(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgLte* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceCmp(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgGt* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceCmp(vtxp));
|
2025-08-10 19:14:02 +02:00
|
|
|
}
|
|
|
|
|
void visit(DfgGte* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
2025-08-15 09:20:36 +02:00
|
|
|
SET_RESULT(traceCmp(vtxp));
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgShiftR* vtxp) override {
|
|
|
|
|
DfgVertex* const lhsp = vtxp->lhsp();
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const DfgConst* const rConstp = vtxp->rhsp()->cast<DfgConst>()) {
|
2025-07-11 20:19:09 +02:00
|
|
|
const uint32_t shiftAmnt = rConstp->toU32();
|
|
|
|
|
// Width of lower half of result
|
|
|
|
|
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt;
|
|
|
|
|
|
|
|
|
|
// If the traced bits are wholly in the input
|
|
|
|
|
if (lowerWidth > m_msb) {
|
|
|
|
|
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If the traced bits are wholly in the extension
|
|
|
|
|
if (m_lsb >= lowerWidth) {
|
|
|
|
|
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// The traced bits span both sides
|
|
|
|
|
if (DfgVertex* const sp = trace(lhsp, lowerWidth - 1 + shiftAmnt, m_lsb + shiftAmnt)) {
|
|
|
|
|
DfgExtend* const resp = make<DfgExtend>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->srcp(sp);
|
|
|
|
|
SET_RESULT(resp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The shift amount is non-negative, so we can conclude zero if all
|
|
|
|
|
// bits at and above the LSB we seek are zeroes
|
|
|
|
|
if (knownToBeZeroAtAndAbove(lhsp, m_lsb)) {
|
|
|
|
|
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgShiftL* vtxp) override {
|
|
|
|
|
DfgVertex* const lhsp = vtxp->lhsp();
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const DfgConst* const rConstp = vtxp->rhsp()->cast<DfgConst>()) {
|
2025-07-11 20:19:09 +02:00
|
|
|
const uint32_t shiftAmnt = rConstp->toU32();
|
|
|
|
|
// Width of lower half of result
|
|
|
|
|
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? vtxp->width() : shiftAmnt;
|
|
|
|
|
|
|
|
|
|
// If the traced bits are wholly in the input
|
|
|
|
|
if (m_lsb >= lowerWidth) {
|
|
|
|
|
SET_RESULT(trace(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If the traced bits are wholly in the extension
|
|
|
|
|
if (lowerWidth > m_msb) {
|
|
|
|
|
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// The traced bits span both sides
|
|
|
|
|
if (DfgVertex* const sp = trace(lhsp, m_msb - shiftAmnt, lowerWidth - shiftAmnt)) {
|
|
|
|
|
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->rhsp(make<DfgConst>(vtxp, resp->width() - sp->width()));
|
|
|
|
|
resp->lhsp(sp);
|
|
|
|
|
SET_RESULT(resp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The shift amount is non-negative, so we can conclude zero if all
|
|
|
|
|
// bits at and below the MSB we seek are zeroes
|
|
|
|
|
if (knownToBeZeroAtAndBelow(lhsp, m_msb)) {
|
|
|
|
|
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-10 19:14:02 +02:00
|
|
|
void visit(DfgCond* vtxp) override {
|
|
|
|
|
if (!m_aggressive) return;
|
|
|
|
|
if (DfgVertex* const condp = trace(vtxp->condp(), 0, 0)) {
|
|
|
|
|
if (DfgVertex* const thenp = trace(vtxp->thenp(), m_msb, m_lsb)) {
|
|
|
|
|
if (DfgVertex* const elsep = trace(vtxp->elsep(), m_msb, m_lsb)) {
|
|
|
|
|
DfgCond* const resp = make<DfgCond>(vtxp, m_msb - m_lsb + 1);
|
|
|
|
|
resp->condp(condp);
|
|
|
|
|
resp->thenp(thenp);
|
|
|
|
|
resp->elsep(elsep);
|
|
|
|
|
SET_RESULT(resp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-10 19:46:45 +02:00
|
|
|
#undef SET_RESULT
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTOR
|
2025-08-05 14:03:30 +02:00
|
|
|
TraceDriver(DfgGraph& dfg, uint64_t component, bool aggressive)
|
2025-07-10 19:46:45 +02:00
|
|
|
: m_dfg{dfg}
|
2025-07-11 20:19:09 +02:00
|
|
|
, m_component{component}
|
|
|
|
|
, m_aggressive{aggressive} {
|
2025-08-10 19:14:02 +02:00
|
|
|
#ifdef VL_DEBUG
|
2025-07-10 19:46:45 +02:00
|
|
|
if (v3Global.opt.debugCheck()) {
|
|
|
|
|
m_lineCoverageFile.open( //
|
|
|
|
|
v3Global.opt.makeDir() + "/" + v3Global.opt.prefix()
|
|
|
|
|
+ "__V3DfgBreakCycles-TraceDriver-line-coverage.txt", //
|
|
|
|
|
std::ios_base::out | std::ios_base::app);
|
|
|
|
|
}
|
2025-08-10 19:14:02 +02:00
|
|
|
#endif
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2025-08-05 14:03:30 +02:00
|
|
|
// Given a Vertex that is part of an SCC denoted by vtxp->user<uint64_t>(),
|
2025-07-10 19:46:45 +02:00
|
|
|
// return a vertex that is equivalent to 'vtxp[lsb +: width]', but is not
|
|
|
|
|
// part of the same SCC. Returns nullptr if such a vertex cannot be
|
2025-07-11 20:19:09 +02:00
|
|
|
// computed. This can add new vertices to the graph. The 'aggressive' flag
|
|
|
|
|
// enables creating many intermediate operations. This should only be set
|
|
|
|
|
// if it is reasonably certain the tracing will succeed, otherwise we can
|
|
|
|
|
// waste a lot of compute.
|
|
|
|
|
static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width,
|
|
|
|
|
bool aggressive) {
|
2025-08-05 14:03:30 +02:00
|
|
|
TraceDriver traceDriver{dfg, vtxp->getUser<uint64_t>(), aggressive};
|
2025-07-10 19:46:45 +02:00
|
|
|
// Find the out-of-component driver of the given vertex
|
|
|
|
|
DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb);
|
|
|
|
|
// Delete unused newly created vertices (these can be created if a
|
|
|
|
|
// partial trace succeded, but an eventual one falied). Because new
|
|
|
|
|
// vertices should be created depth first, it is enough to do a single
|
|
|
|
|
// reverse pass over the collectoin
|
2025-08-20 19:21:24 +02:00
|
|
|
for (DfgVertex* const newp : vlstd::reverse_view(traceDriver.m_newVtxps)) {
|
2025-07-10 19:46:45 +02:00
|
|
|
// Keep the actual result!
|
2025-08-20 19:21:24 +02:00
|
|
|
if (newp == resultp) continue;
|
2025-07-10 19:46:45 +02:00
|
|
|
// Keep used ones!
|
2025-08-20 19:21:24 +02:00
|
|
|
if (newp->hasSinks()) continue;
|
2025-07-10 19:46:45 +02:00
|
|
|
// Delete it
|
2025-08-20 19:21:24 +02:00
|
|
|
VL_DO_DANGLING(newp->unlinkDelete(dfg), newp);
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
// Return the result
|
|
|
|
|
return resultp;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
class IndependentBits final : public DfgVisitor {
|
|
|
|
|
// STATE
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t m_component; // The component the start vertex is part of
|
2025-07-11 20:19:09 +02:00
|
|
|
// Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'.
|
|
|
|
|
std::unordered_map<const DfgVertex*, V3Number> m_vtxp2Mask;
|
|
|
|
|
|
|
|
|
|
std::ofstream m_lineCoverageFile; // Line coverage file, just for testing
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
// Retrieve the mask for the given vertex (create it with value 0 if needed)
|
|
|
|
|
V3Number& mask(const DfgVertex* vtxp) {
|
2025-07-21 20:57:02 +02:00
|
|
|
// Look up (or create) mask for 'vtxp'
|
|
|
|
|
auto pair = m_vtxp2Mask.emplace(
|
|
|
|
|
std::piecewise_construct, //
|
|
|
|
|
std::forward_as_tuple(vtxp), //
|
|
|
|
|
std::forward_as_tuple(vtxp->fileline(), static_cast<int>(vtxp->width()), 0));
|
|
|
|
|
// Initialize to all ones if the vertex is part of the same component, otherwise zeroes
|
2025-08-05 14:03:30 +02:00
|
|
|
if (pair.second && vtxp->getUser<uint64_t>() == m_component) {
|
2025-07-21 20:57:02 +02:00
|
|
|
pair.first->second.setAllBits1();
|
|
|
|
|
}
|
|
|
|
|
return pair.first->second;
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use this macro to call 'mask' in 'visit' methods. This also emits
|
|
|
|
|
// a line to m_lineCoverageFile for testing.
|
|
|
|
|
// TODO: Use C++20 std::source_location instead of a macro
|
2025-08-10 19:14:02 +02:00
|
|
|
#ifdef VL_DEBUG
|
2025-07-11 20:19:09 +02:00
|
|
|
#define MASK(vtxp) \
|
|
|
|
|
([this](const DfgVertex* p) -> V3Number& { \
|
|
|
|
|
if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \
|
|
|
|
|
return mask(p); \
|
|
|
|
|
}(vtxp))
|
2025-08-10 19:14:02 +02:00
|
|
|
#else
|
|
|
|
|
#define MASK(vtxp) mask(vtxp)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Set all bits at or below the most signicant set bit
|
|
|
|
|
void floodTowardsLsb(V3Number& num) {
|
|
|
|
|
bool set = false;
|
|
|
|
|
for (int i = num.width() - 1; i >= 0; --i) {
|
|
|
|
|
if (num.bitIs1(i)) set = true;
|
|
|
|
|
if (set) num.setBit(i, '1');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set all bits at or above the least signicant set bit
|
|
|
|
|
void floodTowardsMsb(V3Number& num) {
|
|
|
|
|
bool set = false;
|
|
|
|
|
for (int i = 0; i < num.width(); ++i) {
|
|
|
|
|
if (num.bitIs1(i)) set = true;
|
|
|
|
|
if (set) num.setBit(i, '1');
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
void visit(DfgVertex* vtxp) override {
|
2025-08-10 19:14:02 +02:00
|
|
|
UINFO(9, "IndependentBits - Unhandled vertex type: " << vtxp->typeName());
|
2025-07-21 20:57:02 +02:00
|
|
|
// Conservative assumption about all bits being dependent prevails
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
void visit(DfgSplicePacked* vtxp) override {
|
2025-07-11 20:19:09 +02:00
|
|
|
// Combine the masks of all drivers
|
|
|
|
|
V3Number& m = MASK(vtxp);
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->foreachDriver([&](DfgVertex& src, uint32_t lo) {
|
|
|
|
|
m.opSelInto(MASK(&src), lo, src.width());
|
|
|
|
|
return false;
|
2025-07-11 20:19:09 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
void visit(DfgVarPacked* vtxp) override {
|
2025-09-02 17:50:40 +02:00
|
|
|
DfgVertex* const srcp = vtxp->srcp();
|
|
|
|
|
if (srcp && srcp->is<DfgSpliceArray>()) return;
|
|
|
|
|
|
|
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
DfgVertex* const defaultp = vtxp->defaultp();
|
|
|
|
|
if (defaultp) m = MASK(defaultp);
|
|
|
|
|
if (!srcp) return;
|
|
|
|
|
|
|
|
|
|
if (DfgSplicePacked* const splicep = srcp->cast<DfgSplicePacked>()) {
|
|
|
|
|
splicep->foreachDriver([&](DfgVertex& src, uint32_t lo) {
|
|
|
|
|
m.opSelInto(MASK(&src), lo, src.width());
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m = MASK(srcp);
|
2025-07-14 23:09:34 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
void visit(DfgArraySel* vtxp) override {
|
|
|
|
|
// Only constant select
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>();
|
2025-07-22 09:23:45 +02:00
|
|
|
if (!idxp) return;
|
|
|
|
|
// From a variable
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgVarArray* varp = vtxp->fromp()->cast<DfgVarArray>();
|
2025-07-22 09:23:45 +02:00
|
|
|
if (!varp) return;
|
|
|
|
|
// Skip through intermediate variables
|
|
|
|
|
while (varp->srcp() && varp->srcp()->is<DfgVarArray>()) {
|
|
|
|
|
varp = varp->srcp()->as<DfgVarArray>();
|
|
|
|
|
}
|
|
|
|
|
// Find driver
|
|
|
|
|
if (!varp->srcp()) return;
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgSpliceArray* const splicep = varp->srcp()->cast<DfgSpliceArray>();
|
2025-07-22 09:23:45 +02:00
|
|
|
if (!splicep) return;
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT());
|
2025-07-22 09:23:45 +02:00
|
|
|
if (!driverp) return;
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>();
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (!uap) return;
|
2025-07-22 09:23:45 +02:00
|
|
|
// Update mask
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
MASK(vtxp) = MASK(uap->srcp());
|
2025-07-22 09:23:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
void visit(DfgConcat* vtxp) override {
|
|
|
|
|
const DfgVertex* const rhsp = vtxp->rhsp();
|
|
|
|
|
const DfgVertex* const lhsp = vtxp->lhsp();
|
|
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
m.opSelInto(MASK(rhsp), 0, rhsp->width());
|
|
|
|
|
m.opSelInto(MASK(lhsp), rhsp->width(), lhsp->width());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgReplicate* vtxp) override {
|
|
|
|
|
const uint32_t count = vtxp->countp()->as<DfgConst>()->toU32();
|
|
|
|
|
const DfgVertex* const srcp = vtxp->srcp();
|
|
|
|
|
const uint32_t sWidth = srcp->width();
|
|
|
|
|
V3Number& vMask = MASK(vtxp);
|
|
|
|
|
V3Number& sMask = MASK(srcp);
|
|
|
|
|
for (uint32_t i = 0; i < count; ++i) vMask.opSelInto(sMask, i * sWidth, sWidth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgSel* vtxp) override {
|
|
|
|
|
const uint32_t lsb = vtxp->lsb();
|
|
|
|
|
const uint32_t msb = lsb + vtxp->width() - 1;
|
|
|
|
|
MASK(vtxp).opSel(MASK(vtxp->fromp()), msb, lsb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgExtend* vtxp) override {
|
|
|
|
|
const DfgVertex* const srcp = vtxp->srcp();
|
2025-07-21 20:57:02 +02:00
|
|
|
const uint32_t sWidth = srcp->width();
|
|
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
m.opSelInto(MASK(srcp), 0, sWidth);
|
|
|
|
|
m.opSetRange(sWidth, vtxp->width() - sWidth, '0');
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgNot* vtxp) override { //
|
|
|
|
|
MASK(vtxp) = MASK(vtxp->srcp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgAnd* vtxp) override { //
|
|
|
|
|
MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgOr* vtxp) override { //
|
|
|
|
|
MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgXor* vtxp) override { //
|
|
|
|
|
MASK(vtxp).opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-10 19:14:02 +02:00
|
|
|
void visit(DfgAdd* vtxp) override {
|
|
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
m.opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
|
|
|
|
|
floodTowardsMsb(m);
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgSub* vtxp) override { // Same as Add: 2's complement (a - b) == (a + ~b + 1)
|
|
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
m.opOr(MASK(vtxp->lhsp()), MASK(vtxp->rhsp()));
|
|
|
|
|
floodTowardsMsb(m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgEq* vtxp) override {
|
|
|
|
|
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero();
|
|
|
|
|
MASK(vtxp).setBit(0, independent ? '0' : '1');
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgNeq* vtxp) override {
|
|
|
|
|
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero();
|
|
|
|
|
MASK(vtxp).setBit(0, independent ? '0' : '1');
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgLt* vtxp) override {
|
|
|
|
|
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero();
|
|
|
|
|
MASK(vtxp).setBit(0, independent ? '0' : '1');
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgLte* vtxp) override {
|
|
|
|
|
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero();
|
|
|
|
|
MASK(vtxp).setBit(0, independent ? '0' : '1');
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgGt* vtxp) override {
|
|
|
|
|
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero();
|
|
|
|
|
MASK(vtxp).setBit(0, independent ? '0' : '1');
|
|
|
|
|
}
|
|
|
|
|
void visit(DfgGte* vtxp) override {
|
|
|
|
|
const bool independent = MASK(vtxp->lhsp()).isEqZero() && MASK(vtxp->rhsp()).isEqZero();
|
|
|
|
|
MASK(vtxp).setBit(0, independent ? '0' : '1');
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
void visit(DfgShiftR* vtxp) override {
|
|
|
|
|
DfgVertex* const rhsp = vtxp->rhsp();
|
|
|
|
|
DfgVertex* const lhsp = vtxp->lhsp();
|
|
|
|
|
const uint32_t width = vtxp->width();
|
|
|
|
|
|
|
|
|
|
// Constant shift can be computed precisely
|
|
|
|
|
if (DfgConst* const rConstp = rhsp->cast<DfgConst>()) {
|
|
|
|
|
const uint32_t shiftAmount = rConstp->toU32();
|
2025-07-21 20:57:02 +02:00
|
|
|
V3Number& m = MASK(vtxp);
|
2025-08-10 19:14:02 +02:00
|
|
|
if (shiftAmount >= width) {
|
|
|
|
|
m.setAllBits0();
|
|
|
|
|
} else {
|
|
|
|
|
m.opShiftR(MASK(lhsp), rConstp->num());
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, as the shift amount is non-negative, any bit at or below
|
|
|
|
|
// the most significant dependent bit might be dependent
|
2025-08-10 19:14:02 +02:00
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
m = MASK(lhsp);
|
|
|
|
|
floodTowardsLsb(m);
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgShiftL* vtxp) override {
|
|
|
|
|
DfgVertex* const rhsp = vtxp->rhsp();
|
|
|
|
|
DfgVertex* const lhsp = vtxp->lhsp();
|
|
|
|
|
const uint32_t width = vtxp->width();
|
|
|
|
|
|
|
|
|
|
// Constant shift can be computed precisely
|
|
|
|
|
if (DfgConst* const rConstp = rhsp->cast<DfgConst>()) {
|
|
|
|
|
const uint32_t shiftAmount = rConstp->toU32();
|
2025-07-21 20:57:02 +02:00
|
|
|
V3Number& m = MASK(vtxp);
|
2025-08-10 19:14:02 +02:00
|
|
|
if (shiftAmount >= width) {
|
|
|
|
|
m.setAllBits0();
|
|
|
|
|
} else {
|
|
|
|
|
m.opShiftL(MASK(lhsp), rConstp->num());
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, as the shift amount is non-negative, any bit at or above
|
|
|
|
|
// the least significant dependent bit might be dependent
|
2025-08-10 19:14:02 +02:00
|
|
|
V3Number& m = MASK(vtxp);
|
|
|
|
|
m = MASK(lhsp);
|
|
|
|
|
floodTowardsMsb(m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(DfgCond* vtxp) override {
|
|
|
|
|
if (!MASK(vtxp->condp()).isEqZero()) {
|
|
|
|
|
MASK(vtxp).setAllBits1();
|
|
|
|
|
} else {
|
|
|
|
|
MASK(vtxp).opOr(MASK(vtxp->thenp()), MASK(vtxp->elsep()));
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef MASK
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTOR
|
2025-07-21 20:57:02 +02:00
|
|
|
IndependentBits(DfgGraph& dfg, DfgVertex* vtxp)
|
2025-08-05 14:03:30 +02:00
|
|
|
: m_component{vtxp->getUser<uint64_t>()} {
|
2025-08-10 19:14:02 +02:00
|
|
|
|
|
|
|
|
#ifdef VL_DEBUG
|
2025-07-11 20:19:09 +02:00
|
|
|
if (v3Global.opt.debugCheck()) {
|
|
|
|
|
m_lineCoverageFile.open( //
|
|
|
|
|
v3Global.opt.makeDir() + "/" + v3Global.opt.prefix()
|
|
|
|
|
+ "__V3DfgBreakCycles-IndependentBits-line-coverage.txt", //
|
|
|
|
|
std::ios_base::out | std::ios_base::app);
|
|
|
|
|
}
|
2025-08-10 19:14:02 +02:00
|
|
|
#endif
|
2025-07-11 20:19:09 +02:00
|
|
|
|
2025-07-21 20:57:02 +02:00
|
|
|
// Work list for the traversal
|
|
|
|
|
std::deque<DfgVertex*> workList;
|
|
|
|
|
|
|
|
|
|
// Enqueue every operation vertex in the analysed component
|
|
|
|
|
for (DfgVertex& vtx : dfg.opVertices()) {
|
2025-08-05 14:03:30 +02:00
|
|
|
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
2025-07-21 20:57:02 +02:00
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
|
2025-07-21 20:57:02 +02:00
|
|
|
// While there is an item on the worklist ...
|
|
|
|
|
while (!workList.empty()) {
|
2025-07-11 20:19:09 +02:00
|
|
|
// Grab next item
|
2025-07-21 20:57:02 +02:00
|
|
|
DfgVertex* const currp = workList.front();
|
|
|
|
|
workList.pop_front();
|
2025-07-11 20:19:09 +02:00
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
if (VN_IS(currp->dtypep(), UnpackArrayDType)) {
|
|
|
|
|
// For an unpacked array vertex, just enque it's sinks.
|
|
|
|
|
// (There can be no loops through arrays directly)
|
2025-09-02 17:50:40 +02:00
|
|
|
currp->foreachSink([&](DfgVertex& vtx) {
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
});
|
2025-07-14 23:09:34 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
// Grab current mask of item
|
|
|
|
|
const V3Number& maskCurr = mask(currp);
|
|
|
|
|
// Remember current mask
|
|
|
|
|
const V3Number prevMask = maskCurr;
|
|
|
|
|
|
|
|
|
|
// Dispatch
|
|
|
|
|
iterate(currp);
|
|
|
|
|
|
|
|
|
|
// If mask changed, enqueue sinks
|
|
|
|
|
if (!prevMask.isCaseEq(maskCurr)) {
|
2025-09-02 17:50:40 +02:00
|
|
|
currp->foreachSink([&](DfgVertex& vtx) {
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
});
|
2025-07-11 20:19:09 +02:00
|
|
|
|
2025-07-21 20:57:02 +02:00
|
|
|
// Check the mask only ever contrects (no bit goes 0 -> 1)
|
2025-07-11 20:19:09 +02:00
|
|
|
if (VL_UNLIKELY(v3Global.opt.debugCheck())) {
|
2025-07-21 20:57:02 +02:00
|
|
|
V3Number notPrev{prevMask};
|
|
|
|
|
notPrev.opNot(prevMask);
|
|
|
|
|
V3Number notPrevAndCurr{maskCurr};
|
|
|
|
|
notPrevAndCurr.opAnd(notPrev, maskCurr);
|
|
|
|
|
UASSERT_OBJ(notPrevAndCurr.isEqZero(), currp, "Mask should only contract");
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2025-08-05 14:03:30 +02:00
|
|
|
// Given a Vertex that is part of an SCC denoted by vtxp->user<uint64_t>(),
|
2025-07-21 20:57:02 +02:00
|
|
|
// compute which bits of this vertex have a value that is independent of
|
|
|
|
|
// the current value of the Vertex itself (simple forward dataflow
|
|
|
|
|
// analysis). Returns a bit mask where a set bit indicates that bit is
|
|
|
|
|
// independent of the vertex itself (logic is not circular). The result is
|
|
|
|
|
// a conservative estimate, so bits reported dependent might not actually
|
|
|
|
|
// be, but all bits reported independent are known to be so.
|
|
|
|
|
static V3Number apply(DfgGraph& dfg, DfgVertex* vtxp) {
|
|
|
|
|
IndependentBits independentBits{dfg, vtxp};
|
2025-07-14 23:09:34 +02:00
|
|
|
// The mask represents the dependent bits, so invert it
|
2025-07-21 20:57:02 +02:00
|
|
|
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
|
|
|
|
result.opNot(independentBits.mask(vtxp));
|
2025-07-11 20:19:09 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FixUpSelDrivers final {
|
2025-07-22 09:23:45 +02:00
|
|
|
static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
2025-07-11 20:19:09 +02:00
|
|
|
size_t nImprovements = 0;
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = vtxp->getUser<uint64_t>();
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->foreachSink([&](DfgVertex& sink) {
|
2025-07-22 09:23:45 +02:00
|
|
|
// Ignore if sink is not part of same cycle
|
2025-09-02 17:50:40 +02:00
|
|
|
if (sink.getUser<uint64_t>() != component) return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
// Only handle Sel
|
2025-07-11 20:19:09 +02:00
|
|
|
DfgSel* const selp = sink.cast<DfgSel>();
|
2025-09-02 17:50:40 +02:00
|
|
|
if (!selp) return false;
|
2025-07-24 16:31:09 +02:00
|
|
|
// Try to find the driver of the selected bits outside the cycle
|
2025-07-11 20:19:09 +02:00
|
|
|
DfgVertex* const fixp
|
2025-07-22 09:23:45 +02:00
|
|
|
= TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false);
|
2025-09-02 17:50:40 +02:00
|
|
|
if (!fixp) return false;
|
2025-07-11 20:19:09 +02:00
|
|
|
// Found an out-of-cycle driver. We can replace this sel with that.
|
|
|
|
|
selp->replaceWith(fixp);
|
|
|
|
|
selp->unlinkDelete(dfg);
|
|
|
|
|
++nImprovements;
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-07-11 20:19:09 +02:00
|
|
|
});
|
2025-07-22 09:23:45 +02:00
|
|
|
return nImprovements;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
|
|
|
|
size_t nImprovements = 0;
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = vtxp->getUser<uint64_t>();
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->foreachSink([&](DfgVertex& sink) {
|
2025-07-22 09:23:45 +02:00
|
|
|
// Ignore if sink is not part of same cycle
|
2025-09-02 17:50:40 +02:00
|
|
|
if (sink.getUser<uint64_t>() != component) return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
// Only handle ArraySels
|
|
|
|
|
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
2025-09-02 17:50:40 +02:00
|
|
|
if (!aselp) return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
// First, try to fix up the whole word
|
2025-09-02 17:50:40 +02:00
|
|
|
if (DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false)) {
|
2025-07-22 09:23:45 +02:00
|
|
|
// Found an out-of-cycle driver for the whole ArraySel
|
|
|
|
|
aselp->replaceWith(fixp);
|
|
|
|
|
aselp->unlinkDelete(dfg);
|
|
|
|
|
++nImprovements;
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
}
|
2025-09-02 17:50:40 +02:00
|
|
|
// Attempt to fix up piece-wise at Sels applied to the ArraySel
|
|
|
|
|
nImprovements += fixUpSelSinks(dfg, aselp);
|
|
|
|
|
// Remove if became unused
|
|
|
|
|
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
|
|
|
|
return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
});
|
|
|
|
|
return nImprovements;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Attempt to push Sel form Var through to the driving
|
|
|
|
|
// expression of the selected bits. This can fix things like
|
|
|
|
|
// 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common.
|
|
|
|
|
// Returns the number of drivers fixed up.
|
|
|
|
|
static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) {
|
|
|
|
|
UINFO(9, "FixUpSelDrivers of " << varp->nodep()->name());
|
|
|
|
|
size_t nImprovements = 0;
|
|
|
|
|
if (varp->is<DfgVarPacked>()) {
|
|
|
|
|
// For Packed variables, fix up all Sels applied to it
|
|
|
|
|
nImprovements += fixUpSelSinks(dfg, varp);
|
|
|
|
|
} else if (varp->is<DfgVarArray>()) {
|
|
|
|
|
// For Array variables, fix up each ArraySel applied to it
|
|
|
|
|
nImprovements += fixUpArraySelSinks(dfg, varp);
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
UINFO(9, "FixUpSelDrivers made " << nImprovements << " improvements");
|
|
|
|
|
return nImprovements;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FixUpIndependentRanges final {
|
2025-07-22 09:23:45 +02:00
|
|
|
// Returns a bitmask set if that bit of 'vtxp' is used (has a sink)
|
|
|
|
|
static V3Number computeUsedBits(DfgVertex* vtxp) {
|
|
|
|
|
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
2025-09-02 17:50:40 +02:00
|
|
|
vtxp->foreachSink([&result](DfgVertex& sink) {
|
2025-07-11 20:19:09 +02:00
|
|
|
// If used via a Sel, mark the selected bits used
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const DfgSel* const selp = sink.cast<DfgSel>()) {
|
2025-07-11 20:19:09 +02:00
|
|
|
uint32_t lsb = selp->lsb();
|
|
|
|
|
uint32_t msb = lsb + selp->width() - 1;
|
|
|
|
|
for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1');
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
// Used without a Sel, so all bits are used
|
|
|
|
|
result.setAllBits1();
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-07-11 20:19:09 +02:00
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
static std::string debugStr(const DfgVertex* vtxp) {
|
|
|
|
|
if (const DfgArraySel* const aselp = vtxp->cast<DfgArraySel>()) {
|
2025-07-22 09:23:45 +02:00
|
|
|
const size_t i = aselp->bitp()->as<DfgConst>()->toSizeT();
|
|
|
|
|
return debugStr(aselp->fromp()) + "[" + std::to_string(i) + "]";
|
|
|
|
|
}
|
2025-08-20 19:21:24 +02:00
|
|
|
if (const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>()) {
|
2025-07-22 09:23:45 +02:00
|
|
|
return varp->nodep()->name();
|
|
|
|
|
}
|
|
|
|
|
vtxp->v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trace drivers of independent bits of 'vtxp' in the range '[hi:lo]'
|
2025-07-11 20:19:09 +02:00
|
|
|
// append replacement terms to 'termps'. Returns number of successful
|
|
|
|
|
// replacements.
|
|
|
|
|
static size_t gatherTerms(std::vector<DfgVertex*>& termps, DfgGraph& dfg,
|
2025-07-22 09:23:45 +02:00
|
|
|
DfgVertex* const vtxp, const V3Number& indpBits, const uint32_t hi,
|
|
|
|
|
const uint32_t lo) {
|
2025-07-11 20:19:09 +02:00
|
|
|
size_t nImprovements = 0;
|
|
|
|
|
// Iterate through consecutive dependent/non-dependet ranges within [hi:lo]
|
|
|
|
|
bool isIndependent = indpBits.bitIs1(lo);
|
|
|
|
|
uint32_t lsb = lo;
|
|
|
|
|
for (uint32_t msb = lo; msb <= hi; ++msb) {
|
|
|
|
|
const bool endRange = msb == hi || isIndependent != indpBits.bitIs1(msb + 1);
|
|
|
|
|
if (!endRange) continue;
|
|
|
|
|
const uint32_t width = msb - lsb + 1;
|
|
|
|
|
DfgVertex* termp = nullptr;
|
|
|
|
|
// If the range is not self-dependent, attempt to trace its driver
|
|
|
|
|
if (isIndependent) {
|
2025-07-22 09:23:45 +02:00
|
|
|
termp = TraceDriver::apply(dfg, vtxp, lsb, width, true);
|
2025-07-11 20:19:09 +02:00
|
|
|
if (termp) {
|
|
|
|
|
++nImprovements;
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(5, "TraceDriver of independent range failed for "
|
2025-07-22 09:23:45 +02:00
|
|
|
<< debugStr(vtxp) << "[" << msb << ":" << lsb << "]");
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Fall back on using the part of the variable (if dependent, or trace failed)
|
|
|
|
|
if (!termp) {
|
2025-09-02 17:50:40 +02:00
|
|
|
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
2025-07-22 09:23:45 +02:00
|
|
|
DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep};
|
2025-07-24 16:31:09 +02:00
|
|
|
// Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp'
|
2025-08-05 14:03:30 +02:00
|
|
|
selp->setUser<uint64_t>(vtxp->getUser<uint64_t>());
|
2025-07-22 09:23:45 +02:00
|
|
|
// Do not connect selp->fromp yet, need to do afer replacing 'vtxp'
|
2025-07-11 20:19:09 +02:00
|
|
|
selp->lsb(lsb);
|
|
|
|
|
termp = selp;
|
|
|
|
|
}
|
|
|
|
|
// Record the term
|
|
|
|
|
termps.emplace_back(termp);
|
|
|
|
|
// Next iteration
|
|
|
|
|
isIndependent = !isIndependent;
|
|
|
|
|
lsb = msb + 1;
|
|
|
|
|
}
|
|
|
|
|
return nImprovements;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
static size_t fixUpPacked(DfgGraph& dfg, DfgVertex* vtxp) {
|
|
|
|
|
UASSERT_OBJ(VN_IS(vtxp->dtypep(), BasicDType), vtxp, "Should be a packed BasicDType");
|
2025-07-11 20:19:09 +02:00
|
|
|
size_t nImprovements = 0;
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
// Figure out which bits of 'vtxp' are used
|
|
|
|
|
const V3Number usedBits = computeUsedBits(vtxp);
|
|
|
|
|
UINFO(9, "Used bits of '" << debugStr(vtxp) << "' are "
|
|
|
|
|
<< usedBits.displayed(vtxp->fileline(), "%b"));
|
2025-07-11 20:19:09 +02:00
|
|
|
// Nothing to do if no bits are used
|
|
|
|
|
if (usedBits.isEqZero()) return 0;
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
// Figure out which bits of 'vtxp' are dependent of themselves
|
|
|
|
|
const V3Number indpBits = IndependentBits::apply(dfg, vtxp);
|
|
|
|
|
UINFO(9, "Independent bits of '" << debugStr(vtxp) << "' are "
|
|
|
|
|
<< indpBits.displayed(vtxp->fileline(), "%b"));
|
2025-07-11 20:19:09 +02:00
|
|
|
// Can't do anything if all bits are dependent
|
|
|
|
|
if (indpBits.isEqZero()) return 0;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Nothing to do if no used bits are independen (all used bits are dependent)
|
2025-07-22 09:23:45 +02:00
|
|
|
V3Number usedAndIndependent{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
2025-07-11 20:19:09 +02:00
|
|
|
usedAndIndependent.opAnd(usedBits, indpBits);
|
|
|
|
|
if (usedAndIndependent.isEqZero()) return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
// We are computing the terms to concatenate and replace 'vtxp' with
|
2025-07-11 20:19:09 +02:00
|
|
|
std::vector<DfgVertex*> termps;
|
|
|
|
|
|
|
|
|
|
// Iterate through consecutive used/unused ranges
|
2025-07-22 09:23:45 +02:00
|
|
|
FileLine* const flp = vtxp->fileline();
|
|
|
|
|
const uint32_t width = vtxp->width();
|
2025-07-11 20:19:09 +02:00
|
|
|
bool isUsed = usedBits.bitIs1(0); // Is current range used
|
|
|
|
|
uint32_t lsb = 0; // LSB of current range
|
|
|
|
|
for (uint32_t msb = 0; msb < width; ++msb) {
|
|
|
|
|
const bool endRange = msb == width - 1 || isUsed != usedBits.bitIs1(msb + 1);
|
|
|
|
|
if (!endRange) continue;
|
|
|
|
|
if (isUsed) {
|
|
|
|
|
// The range is used, compute the replacement terms
|
2025-07-22 09:23:45 +02:00
|
|
|
nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb);
|
2025-07-11 20:19:09 +02:00
|
|
|
} else {
|
|
|
|
|
// The range is not used, just use constant 0 as a placeholder
|
2025-09-02 17:50:40 +02:00
|
|
|
DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1, 0};
|
2025-08-05 14:03:30 +02:00
|
|
|
constp->setUser<uint64_t>(0);
|
2025-07-24 16:31:09 +02:00
|
|
|
termps.emplace_back(constp);
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
// Next iteration
|
|
|
|
|
isUsed = !isUsed;
|
|
|
|
|
lsb = msb + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 09:20:36 +02:00
|
|
|
// If no imporovement was possible, delete the terms we just created
|
|
|
|
|
if (!nImprovements) {
|
2025-08-20 19:21:24 +02:00
|
|
|
for (DfgVertex* const termp : termps) VL_DO_DANGLING(termp->unlinkDelete(dfg), termp);
|
2025-08-15 09:20:36 +02:00
|
|
|
termps.clear();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2025-07-11 20:19:09 +02:00
|
|
|
|
2025-08-15 09:20:36 +02:00
|
|
|
// We managed to imporove something, apply the replacement
|
|
|
|
|
// Concatenate all the terms to create the replacement
|
|
|
|
|
DfgVertex* replacementp = termps.front();
|
|
|
|
|
const uint64_t vComp = vtxp->getUser<uint64_t>();
|
|
|
|
|
for (size_t i = 1; i < termps.size(); ++i) {
|
|
|
|
|
DfgVertex* const termp = termps[i];
|
|
|
|
|
const uint32_t catWidth = replacementp->width() + termp->width();
|
2025-09-02 17:50:40 +02:00
|
|
|
AstNodeDType* const dtypep = V3Dfg::dtypePacked(catWidth);
|
2025-08-15 09:20:36 +02:00
|
|
|
DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep};
|
|
|
|
|
catp->rhsp(replacementp);
|
|
|
|
|
catp->lhsp(termp);
|
|
|
|
|
// Need to figure out which component the replacement vertex
|
|
|
|
|
// belongs to. If any of the terms are of the original
|
|
|
|
|
// component, it's part of that component, otherwise its not
|
|
|
|
|
// cyclic (all terms are from outside the original component,
|
|
|
|
|
// and feed into the original component).
|
|
|
|
|
const uint64_t tComp = termp->getUser<uint64_t>();
|
|
|
|
|
const uint64_t rComp = replacementp->getUser<uint64_t>();
|
|
|
|
|
catp->setUser<uint64_t>(tComp == vComp || rComp == vComp ? vComp : 0);
|
|
|
|
|
replacementp = catp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace the vertex
|
|
|
|
|
vtxp->replaceWith(replacementp);
|
|
|
|
|
// Connect the Sel nodes in the replacement
|
|
|
|
|
for (DfgVertex* const termp : termps) {
|
|
|
|
|
if (DfgSel* const selp = termp->cast<DfgSel>()) {
|
|
|
|
|
if (!selp->fromp()) selp->fromp(vtxp);
|
2025-07-11 20:19:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 09:20:36 +02:00
|
|
|
// Done
|
2025-07-22 09:23:45 +02:00
|
|
|
return nImprovements;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Similar to FixUpSelDrivers, but first comptute which bits of the
|
|
|
|
|
// variable are self dependent, and fix up those that are independent
|
|
|
|
|
// but used.
|
|
|
|
|
static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) {
|
|
|
|
|
UINFO(9, "FixUpIndependentRanges of " << varp->nodep()->name());
|
|
|
|
|
size_t nImprovements = 0;
|
|
|
|
|
|
|
|
|
|
if (varp->is<DfgVarPacked>()) {
|
|
|
|
|
// For Packed variables, fix up as whole
|
|
|
|
|
nImprovements += fixUpPacked(dfg, varp);
|
|
|
|
|
} else if (varp->is<DfgVarArray>()) {
|
|
|
|
|
// For array variables, fix up element-wise
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = varp->getUser<uint64_t>();
|
2025-09-02 17:50:40 +02:00
|
|
|
varp->foreachSink([&](DfgVertex& sink) {
|
2025-07-24 16:31:09 +02:00
|
|
|
// Ignore if sink is not part of same cycle
|
2025-09-02 17:50:40 +02:00
|
|
|
if (sink.getUser<uint64_t>() != component) return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
// Only handle ArraySels with constant index
|
|
|
|
|
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
2025-09-02 17:50:40 +02:00
|
|
|
if (!aselp) return false;
|
|
|
|
|
if (!aselp->bitp()->is<DfgConst>()) return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
// Fix up the word
|
|
|
|
|
nImprovements += fixUpPacked(dfg, aselp);
|
|
|
|
|
// Remove if became unused
|
|
|
|
|
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-07-22 09:23:45 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
UINFO(9, "FixUpIndependentRanges made " << nImprovements << " improvements");
|
|
|
|
|
return nImprovements;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-26 21:37:01 +02:00
|
|
|
std::pair<std::unique_ptr<DfgGraph>, bool> //
|
|
|
|
|
V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
|
2025-07-10 19:46:45 +02:00
|
|
|
// Shorthand for dumping graph at given dump level
|
|
|
|
|
const auto dump = [&](int level, const DfgGraph& dfg, const std::string& name) {
|
|
|
|
|
if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-" + name);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Can't do much with trivial things ('a = a' or 'a[1] = a[0]'), so bail
|
|
|
|
|
if (dfg.size() <= 2) {
|
|
|
|
|
UINFO(7, "Graph is trivial");
|
|
|
|
|
dump(9, dfg, "trivial");
|
|
|
|
|
++ctx.m_breakCyclesContext.m_nTrivial;
|
|
|
|
|
return {nullptr, false};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show input for debugging
|
|
|
|
|
dump(7, dfg, "input");
|
|
|
|
|
|
|
|
|
|
// We might fail to make any improvements, so first create a clone of the
|
|
|
|
|
// graph. This is what we will be working on, and return if successful.
|
|
|
|
|
// Do not touch the input graph.
|
|
|
|
|
std::unique_ptr<DfgGraph> resultp = dfg.clone();
|
|
|
|
|
// Just shorthand for code below
|
|
|
|
|
DfgGraph& res = *resultp;
|
|
|
|
|
dump(9, res, "clone");
|
|
|
|
|
|
|
|
|
|
// How many improvements have we made
|
|
|
|
|
size_t nImprovements = 0;
|
|
|
|
|
size_t prevNImprovements;
|
|
|
|
|
|
|
|
|
|
// Iterate while an improvement can be made and the graph is still cyclic
|
|
|
|
|
do {
|
2025-08-05 14:03:30 +02:00
|
|
|
// Color SCCs (populates DfgVertex::user<uint64_t>())
|
2025-07-10 19:46:45 +02:00
|
|
|
const auto userDataInUse = res.userDataInUse();
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(res);
|
2025-07-10 19:46:45 +02:00
|
|
|
|
|
|
|
|
// Congrats if it has become acyclic
|
|
|
|
|
if (!numNonTrivialSCCs) {
|
|
|
|
|
UINFO(7, "Graph became acyclic after " << nImprovements << " improvements");
|
|
|
|
|
dump(7, res, "result-acyclic");
|
|
|
|
|
++ctx.m_breakCyclesContext.m_nFixed;
|
|
|
|
|
return {std::move(resultp), true};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Attempt new improvements
|
|
|
|
|
UINFO(9, "New iteration after " << nImprovements << " improvements");
|
|
|
|
|
prevNImprovements = nImprovements;
|
|
|
|
|
|
2025-07-11 20:19:09 +02:00
|
|
|
// Method 1: FixUpSelDrivers
|
2025-07-10 19:46:45 +02:00
|
|
|
for (DfgVertexVar& vtx : res.varVertices()) {
|
|
|
|
|
// If Variable is not part of a cycle, move on
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = vtx.getUser<uint64_t>();
|
2025-07-10 19:46:45 +02:00
|
|
|
if (!component) continue;
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
const size_t nFixed = FixUpSelDrivers::apply(res, &vtx);
|
2025-07-11 20:19:09 +02:00
|
|
|
if (nFixed) {
|
|
|
|
|
nImprovements += nFixed;
|
|
|
|
|
ctx.m_breakCyclesContext.m_nImprovements += nFixed;
|
|
|
|
|
dump(9, res, "FixUpSelDrivers");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we managed to fix something, try again with the earlier methods
|
|
|
|
|
if (nImprovements != prevNImprovements) continue;
|
|
|
|
|
|
|
|
|
|
// Method 2. FixUpIndependentRanges
|
|
|
|
|
for (DfgVertexVar& vtx : res.varVertices()) {
|
|
|
|
|
// If Variable is not part of a cycle, move on
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = vtx.getUser<uint64_t>();
|
2025-07-11 20:19:09 +02:00
|
|
|
if (!component) continue;
|
|
|
|
|
|
2025-07-22 09:23:45 +02:00
|
|
|
const size_t nFixed = FixUpIndependentRanges::apply(res, &vtx);
|
2025-07-11 20:19:09 +02:00
|
|
|
if (nFixed) {
|
|
|
|
|
nImprovements += nFixed;
|
|
|
|
|
ctx.m_breakCyclesContext.m_nImprovements += nFixed;
|
|
|
|
|
dump(9, res, "FixUpIndependentRanges");
|
|
|
|
|
}
|
2025-07-10 19:46:45 +02:00
|
|
|
}
|
|
|
|
|
} while (nImprovements != prevNImprovements);
|
|
|
|
|
|
2025-08-10 10:19:16 +02:00
|
|
|
if (dumpDfgLevel() >= 9) {
|
|
|
|
|
const auto userDataInUse = res.userDataInUse();
|
|
|
|
|
V3DfgPasses::colorStronglyConnectedComponents(res);
|
|
|
|
|
res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining",
|
|
|
|
|
[](const DfgVertex& vtx) { return vtx.getUser<uint64_t>(); });
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-10 19:46:45 +02:00
|
|
|
// If an improvement was made, return the still cyclic improved graph
|
|
|
|
|
if (nImprovements) {
|
|
|
|
|
UINFO(7, "Graph was improved " << nImprovements << " times");
|
|
|
|
|
dump(7, res, "result-improved");
|
|
|
|
|
++ctx.m_breakCyclesContext.m_nImproved;
|
|
|
|
|
return {std::move(resultp), false};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No improvement was made
|
|
|
|
|
UINFO(7, "Graph NOT improved");
|
|
|
|
|
dump(7, res, "result-original");
|
|
|
|
|
++ctx.m_breakCyclesContext.m_nUnchanged;
|
|
|
|
|
return {nullptr, false};
|
|
|
|
|
}
|