Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Implementations of simple passes over DfgGraph
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
#include "V3DfgPasses.h"
|
|
|
|
|
|
|
|
|
|
#include "V3Dfg.h"
|
2024-03-02 20:49:29 +01:00
|
|
|
#include "V3File.h"
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3String.h"
|
|
|
|
|
|
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2024-03-26 00:06:25 +01:00
|
|
|
void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
|
|
|
|
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
2025-09-10 13:38:49 +02:00
|
|
|
// Nothing to inline it into
|
|
|
|
|
if (!vtx.hasSinks()) continue;
|
|
|
|
|
// Nohting to inline
|
|
|
|
|
DfgVertex* const srcp = vtx.srcp();
|
|
|
|
|
if (!srcp) continue;
|
|
|
|
|
// Value can differ from driver
|
|
|
|
|
if (vtx.isVolatile()) continue;
|
|
|
|
|
// Partial driver cannot be inlined
|
|
|
|
|
if (srcp->is<DfgVertexSplice>()) continue;
|
|
|
|
|
if (srcp->is<DfgUnitArray>()) continue;
|
|
|
|
|
// Okie dokie, here we go ...
|
|
|
|
|
vtx.replaceWith(srcp);
|
2022-10-07 17:13:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
2025-09-03 14:55:33 +02:00
|
|
|
// Worklist based algoritm
|
|
|
|
|
DfgWorklist workList{dfg};
|
2022-11-06 15:13:42 +01:00
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
// Add all unused operation vertices to the work list
|
2024-03-26 00:06:25 +01:00
|
|
|
for (DfgVertex& vtx : dfg.opVertices()) {
|
2025-09-02 23:21:24 +02:00
|
|
|
if (vtx.hasSinks()) continue;
|
|
|
|
|
// This vertex is unused. Add to work list.
|
2025-09-03 14:55:33 +02:00
|
|
|
workList.push_front(vtx);
|
2022-11-06 15:13:42 +01:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Also add all unused temporaries created during synthesis
|
|
|
|
|
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
|
|
|
|
if (!vtx.tmpForp()) continue;
|
2025-09-02 23:21:24 +02:00
|
|
|
if (vtx.hasSinks()) continue;
|
|
|
|
|
if (vtx.hasDfgRefs()) continue;
|
|
|
|
|
// This vertex is unused. Add to work list.
|
2025-09-03 14:55:33 +02:00
|
|
|
workList.push_front(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
|
|
|
}
|
|
|
|
|
|
2022-11-06 15:13:42 +01:00
|
|
|
// Process the work list
|
2025-09-03 14:55:33 +02:00
|
|
|
workList.foreach([&](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
|
|
|
// DfgLogic should have been synthesized or removed
|
2025-09-03 14:55:33 +02:00
|
|
|
UASSERT_OBJ(!vtx.is<DfgLogic>(), &vtx, "Should not be DfgLogic");
|
2022-11-06 15:13:42 +01:00
|
|
|
// If used, then nothing to do, so move on
|
2025-09-03 14:55:33 +02:00
|
|
|
if (vtx.hasSinks()) 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
|
|
|
// If temporary used in another graph, we need to keep it
|
2025-09-03 14:55:33 +02:00
|
|
|
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
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(varp->tmpForp(), varp, "Non-temporary variable should not be visited");
|
2025-09-03 14:55:33 +02:00
|
|
|
if (varp->hasDfgRefs()) 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
|
|
|
}
|
2022-11-06 15:13:42 +01:00
|
|
|
// Add sources of unused vertex to work list
|
2025-09-03 14:55:33 +02:00
|
|
|
vtx.foreachSource([&](DfgVertex& src) {
|
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
|
|
|
// We only remove actual operation vertices and synthesis temporaries in this loop
|
2025-09-02 17:50:40 +02:00
|
|
|
if (src.is<DfgConst>()) 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
|
|
|
const DfgVertexVar* const varp = src.cast<DfgVertexVar>();
|
2025-09-02 17:50:40 +02:00
|
|
|
if (varp && !varp->tmpForp()) return false;
|
2025-09-03 14:55:33 +02:00
|
|
|
// Add source to workList
|
|
|
|
|
workList.push_front(src);
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2022-11-06 15:13:42 +01:00
|
|
|
});
|
|
|
|
|
// Remove the unused vertex
|
2025-09-03 14:55:33 +02:00
|
|
|
vtx.unlinkDelete(dfg);
|
|
|
|
|
});
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
|
2025-08-08 23:53:12 +02:00
|
|
|
// Remove unused and undriven variable vertices
|
|
|
|
|
for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) {
|
|
|
|
|
if (!vtxp->hasSinks() && !vtxp->srcp()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 17:13:01 +02:00
|
|
|
// Finally remove unused constants
|
2024-03-26 00:06:25 +01:00
|
|
|
for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) {
|
|
|
|
|
if (!vtxp->hasSinks()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
2022-10-07 17:13:01 +02:00
|
|
|
}
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-17 00:14:24 +02:00
|
|
|
void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
2025-09-10 13:38:49 +02:00
|
|
|
if (!dfg.modulep()) return; // binToOneHot only works with unscoped DfgGraphs for now
|
2025-07-01 23:55:08 +02:00
|
|
|
|
2025-06-17 00:14:24 +02:00
|
|
|
// Structure to keep track of comparison details
|
|
|
|
|
struct Term final {
|
2025-08-19 23:02:10 +02:00
|
|
|
DfgVertex* m_vtxp = nullptr; // Vertex to replace
|
|
|
|
|
bool m_inv = false; // '!=', instead of '=='
|
2025-06-17 00:14:24 +02:00
|
|
|
Term() = default;
|
|
|
|
|
Term(DfgVertex* vtxp, bool inv)
|
|
|
|
|
: m_vtxp{vtxp}
|
|
|
|
|
, m_inv{inv} {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// List of vertices that are used as sources
|
|
|
|
|
std::vector<DfgVertex*> srcps;
|
|
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
// Map from 'vertices' -> 'value beign compared' -> 'terms'
|
|
|
|
|
using Val2Terms = std::map<uint32_t, std::vector<Term>>;
|
|
|
|
|
DfgUserMap<Val2Terms> vtx2Val2Terms = dfg.makeUserMap<Val2Terms>();
|
|
|
|
|
|
2025-06-17 00:14:24 +02:00
|
|
|
// Only consider input variables from a reasonable range:
|
|
|
|
|
// - not too big to avoid huge tables, you are doomed anyway at that point..
|
|
|
|
|
// - not too small, as it's probably not worth it
|
|
|
|
|
constexpr uint32_t WIDTH_MIN = 7;
|
|
|
|
|
constexpr uint32_t WIDTH_MAX = 20;
|
|
|
|
|
const auto widthOk = [](const DfgVertex* vtxp) {
|
|
|
|
|
const uint32_t width = vtxp->width();
|
|
|
|
|
return WIDTH_MIN <= width && width <= WIDTH_MAX;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Do not convert terms that look like they are in a Cond tree
|
|
|
|
|
// the C++ compiler can generate jump tables for these
|
|
|
|
|
const std::function<bool(const DfgVertex*, bool)> useOk
|
|
|
|
|
= [&](const DfgVertex* vtxp, bool inv) -> bool {
|
|
|
|
|
// Go past a single 'Not' sink, which is common
|
|
|
|
|
if (DfgVertex* const sinkp = vtxp->singleSink()) {
|
|
|
|
|
if (sinkp->is<DfgNot>()) return useOk(sinkp, !inv);
|
|
|
|
|
}
|
2025-09-02 17:50:40 +02:00
|
|
|
const bool condTree = vtxp->foreachSink([&](const DfgVertex& sink) {
|
|
|
|
|
const DfgCond* const condp = sink.cast<DfgCond>();
|
|
|
|
|
if (!condp) return false;
|
|
|
|
|
if (condp->condp() != vtxp) return false;
|
|
|
|
|
return inv ? condp->thenp()->is<DfgCond>() : condp->elsep()->is<DfgCond>();
|
2025-06-17 00:14:24 +02:00
|
|
|
});
|
2025-09-02 17:50:40 +02:00
|
|
|
return !condTree;
|
2025-06-17 00:14:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Look at all comparison nodes and build the 'Val2Terms' map for each source vertex
|
|
|
|
|
uint32_t nTerms = 0;
|
|
|
|
|
for (DfgVertex& vtx : dfg.opVertices()) {
|
|
|
|
|
DfgVertex* srcp = nullptr;
|
|
|
|
|
uint32_t val = 0;
|
|
|
|
|
bool inv = false;
|
|
|
|
|
if (DfgEq* const eqp = vtx.cast<DfgEq>()) {
|
|
|
|
|
DfgConst* const constp = eqp->lhsp()->cast<DfgConst>();
|
|
|
|
|
if (!constp || !widthOk(constp) || !useOk(eqp, false)) continue;
|
|
|
|
|
srcp = eqp->rhsp();
|
|
|
|
|
val = constp->toU32();
|
|
|
|
|
inv = false;
|
|
|
|
|
} else if (DfgNeq* const neqp = vtx.cast<DfgNeq>()) {
|
|
|
|
|
DfgConst* const constp = neqp->lhsp()->cast<DfgConst>();
|
|
|
|
|
if (!constp || !widthOk(constp) || !useOk(neqp, true)) continue;
|
|
|
|
|
srcp = neqp->rhsp();
|
|
|
|
|
val = constp->toU32();
|
|
|
|
|
inv = true;
|
|
|
|
|
} else if (DfgRedAnd* const redAndp = vtx.cast<DfgRedAnd>()) {
|
|
|
|
|
srcp = redAndp->srcp();
|
|
|
|
|
if (!widthOk(srcp) || !useOk(redAndp, false)) continue;
|
|
|
|
|
val = (1U << srcp->width()) - 1;
|
|
|
|
|
inv = false;
|
|
|
|
|
} else if (DfgRedOr* const redOrp = vtx.cast<DfgRedOr>()) {
|
|
|
|
|
srcp = redOrp->srcp();
|
|
|
|
|
if (!widthOk(srcp) || !useOk(redOrp, true)) continue;
|
|
|
|
|
val = 0;
|
|
|
|
|
inv = true;
|
|
|
|
|
} else {
|
|
|
|
|
// Not a comparison-like vertex
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-09-02 23:21:24 +02:00
|
|
|
// Grab Val2Terms for this vertex
|
|
|
|
|
Val2Terms& val2Terms = vtx2Val2Terms[srcp];
|
|
|
|
|
// Remeber and on first encounter
|
|
|
|
|
if (val2Terms.empty()) srcps.emplace_back(srcp);
|
2025-06-17 00:14:24 +02:00
|
|
|
// Record term
|
2025-09-02 23:21:24 +02:00
|
|
|
val2Terms[val].emplace_back(&vtx, inv);
|
2025-06-17 00:14:24 +02:00
|
|
|
++nTerms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Somewhat arbitrarily, only apply if more than 64 unique comparisons are required
|
|
|
|
|
constexpr uint32_t TERM_LIMIT = 65;
|
|
|
|
|
// This should hold, otherwise we do redundant work gathering terms that will never be used
|
|
|
|
|
static_assert((1U << WIDTH_MIN) >= TERM_LIMIT, "TERM_LIMIT too big relative to 2**WIDTH_MIN");
|
|
|
|
|
|
|
|
|
|
// Fast path exit if we surely don't need to convet anything
|
|
|
|
|
if (nTerms < TERM_LIMIT) return;
|
|
|
|
|
|
|
|
|
|
// Sequence numbers for name generation
|
|
|
|
|
size_t nTables = 0;
|
|
|
|
|
|
|
|
|
|
// Create decoders for each srcp
|
|
|
|
|
for (DfgVertex* const srcp : srcps) {
|
2025-09-02 23:21:24 +02:00
|
|
|
const Val2Terms& val2Terms = vtx2Val2Terms[srcp];
|
2025-06-17 00:14:24 +02:00
|
|
|
|
|
|
|
|
// If not enough terms in this vertex, ignore
|
|
|
|
|
if (val2Terms.size() < TERM_LIMIT) continue;
|
|
|
|
|
|
|
|
|
|
// Width of the decoded binary value
|
|
|
|
|
const uint32_t width = srcp->width();
|
|
|
|
|
// Number of bits in the input operand
|
|
|
|
|
const uint32_t nBits = 1U << width;
|
|
|
|
|
|
|
|
|
|
// Construct the decoder by converting many "const == vtx" by:
|
|
|
|
|
// - Adding a single decoder block, where 'tab' is zero initialized:
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// tab[pre] = 0;
|
|
|
|
|
// tab[vtx] = 1;
|
|
|
|
|
// pre = vtx;
|
|
|
|
|
// end
|
|
|
|
|
// We mark 'pre' so the write is ignored during scheduling, so this
|
|
|
|
|
// won't cause a combinational cycle.
|
|
|
|
|
// Note that albeit this looks like partial udpates to 'tab', the
|
|
|
|
|
// actual result is that only one value in 'tab' is ever one, while
|
|
|
|
|
// all the others are always zero.
|
|
|
|
|
// - and replace the comparisons with 'tab[const]'
|
|
|
|
|
|
|
|
|
|
FileLine* const flp = srcp->fileline();
|
|
|
|
|
|
|
|
|
|
// Required data types
|
2025-09-07 21:38:50 +02:00
|
|
|
const DfgDataType& idxDType = srcp->dtype();
|
|
|
|
|
const DfgDataType& bitDType = DfgDataType::packed(1);
|
|
|
|
|
const DfgDataType& tabDType = DfgDataType::array(bitDType, nBits);
|
2025-06-17 00:14:24 +02:00
|
|
|
|
|
|
|
|
// The index variable
|
2025-08-20 19:21:24 +02:00
|
|
|
AstVar* const idxVarp = [&]() {
|
2025-06-17 00:14:24 +02:00
|
|
|
// If there is an existing result variable, use that, otherwise create a new variable
|
2025-07-14 23:09:34 +02:00
|
|
|
DfgVarPacked* varp = nullptr;
|
|
|
|
|
if (DfgVertexVar* const vp = srcp->getResultVar()) {
|
|
|
|
|
varp = vp->as<DfgVarPacked>();
|
|
|
|
|
} else {
|
2025-06-17 00:14:24 +02:00
|
|
|
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
2025-09-07 21:38:50 +02:00
|
|
|
varp = dfg.makeNewVar(flp, name, idxDType, nullptr)->as<DfgVarPacked>();
|
2025-06-17 00:14:24 +02:00
|
|
|
varp->varp()->isInternal(true);
|
2025-07-14 23:09:34 +02:00
|
|
|
varp->srcp(srcp);
|
2025-06-17 00:14:24 +02:00
|
|
|
}
|
2025-08-05 11:24:54 +02:00
|
|
|
varp->setHasModRdRefs();
|
2025-08-20 19:21:24 +02:00
|
|
|
return varp->varp();
|
2025-06-17 00:14:24 +02:00
|
|
|
}();
|
|
|
|
|
// The previous index variable - we don't need a vertex for this
|
|
|
|
|
AstVar* const preVarp = [&]() {
|
|
|
|
|
const std::string name = dfg.makeUniqueName("BinToOneHot_Pre", nTables);
|
2025-09-07 21:38:50 +02:00
|
|
|
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, idxDType.astDtypep()};
|
2025-06-17 00:14:24 +02:00
|
|
|
dfg.modulep()->addStmtsp(varp);
|
|
|
|
|
varp->isInternal(true);
|
|
|
|
|
varp->noReset(true);
|
|
|
|
|
varp->setIgnoreSchedWrite();
|
|
|
|
|
return varp;
|
|
|
|
|
}();
|
|
|
|
|
// The table variable
|
|
|
|
|
DfgVarArray* const tabVtxp = [&]() {
|
|
|
|
|
const std::string name = dfg.makeUniqueName("BinToOneHot_Tab", nTables);
|
2025-07-01 23:55:08 +02:00
|
|
|
DfgVarArray* const varp
|
2025-09-07 21:38:50 +02:00
|
|
|
= dfg.makeNewVar(flp, name, tabDType, nullptr)->as<DfgVarArray>();
|
2025-06-17 00:14:24 +02:00
|
|
|
varp->varp()->isInternal(true);
|
|
|
|
|
varp->varp()->noReset(true);
|
2025-08-05 11:24:54 +02:00
|
|
|
varp->setHasModWrRefs();
|
2025-06-17 00:14:24 +02:00
|
|
|
return varp;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
++nTables;
|
|
|
|
|
++ctx.m_decodersCreated;
|
|
|
|
|
|
|
|
|
|
// Initialize 'tab' and 'pre' variables statically
|
|
|
|
|
AstInitialStatic* const initp = new AstInitialStatic{flp, nullptr};
|
|
|
|
|
dfg.modulep()->addStmtsp(initp);
|
|
|
|
|
{ // pre = 0
|
|
|
|
|
initp->addStmtsp(new AstAssign{
|
|
|
|
|
flp, //
|
|
|
|
|
new AstVarRef{flp, preVarp, VAccess::WRITE}, //
|
|
|
|
|
new AstConst{flp, AstConst::WidthedValue{}, static_cast<int>(width), 0}});
|
|
|
|
|
}
|
|
|
|
|
{ // tab.fill(0)
|
|
|
|
|
AstCMethodHard* const callp = new AstCMethodHard{
|
2025-09-27 14:22:17 +02:00
|
|
|
flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE}, VCMethod::UNPACKED_FILL};
|
2025-06-17 00:14:24 +02:00
|
|
|
callp->addPinsp(new AstConst{flp, AstConst::BitFalse{}});
|
|
|
|
|
callp->dtypeSetVoid();
|
|
|
|
|
initp->addStmtsp(callp->makeStmt());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build the decoder logic
|
|
|
|
|
AstAlways* const logicp = new AstAlways{flp, VAlwaysKwd::ALWAYS_COMB, nullptr, nullptr};
|
|
|
|
|
dfg.modulep()->addStmtsp(logicp);
|
|
|
|
|
{ // tab[pre] = 0;
|
|
|
|
|
logicp->addStmtsp(new AstAssign{
|
|
|
|
|
flp, //
|
|
|
|
|
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE},
|
|
|
|
|
new AstVarRef{flp, preVarp, VAccess::READ}}, //
|
|
|
|
|
new AstConst{flp, AstConst::BitFalse{}}});
|
|
|
|
|
}
|
|
|
|
|
{ // tab[idx] = 1
|
|
|
|
|
logicp->addStmtsp(new AstAssign{
|
|
|
|
|
flp, //
|
|
|
|
|
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE},
|
2025-08-20 19:21:24 +02:00
|
|
|
new AstVarRef{flp, idxVarp, VAccess::READ}}, //
|
2025-06-17 00:14:24 +02:00
|
|
|
new AstConst{flp, AstConst::BitTrue{}}});
|
|
|
|
|
}
|
|
|
|
|
{ // pre = idx
|
|
|
|
|
logicp->addStmtsp(new AstAssign{flp, //
|
|
|
|
|
new AstVarRef{flp, preVarp, VAccess::WRITE}, //
|
2025-08-20 19:21:24 +02:00
|
|
|
new AstVarRef{flp, idxVarp, VAccess::READ}});
|
2025-06-17 00:14:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace terms with ArraySels
|
|
|
|
|
for (const auto& pair : val2Terms) {
|
|
|
|
|
const uint32_t val = pair.first;
|
|
|
|
|
const std::vector<Term>& terms = pair.second;
|
|
|
|
|
// Create the ArraySel
|
2025-06-28 18:29:41 +02:00
|
|
|
FileLine* const aflp = terms.front().m_vtxp->fileline();
|
2025-09-07 21:38:50 +02:00
|
|
|
DfgArraySel* const aselp = new DfgArraySel{dfg, aflp, bitDType};
|
2025-06-17 00:14:24 +02:00
|
|
|
aselp->fromp(tabVtxp);
|
2025-06-28 18:29:41 +02:00
|
|
|
aselp->bitp(new DfgConst{dfg, aflp, width, val});
|
2025-06-17 00:14:24 +02:00
|
|
|
// The inverted value, if needed
|
|
|
|
|
DfgNot* notp = nullptr;
|
|
|
|
|
// Repalce the terms
|
|
|
|
|
for (const Term& term : terms) {
|
|
|
|
|
if (term.m_inv) {
|
|
|
|
|
if (!notp) {
|
2025-09-07 21:38:50 +02:00
|
|
|
notp = new DfgNot{dfg, aflp, bitDType};
|
2025-06-17 00:14:24 +02:00
|
|
|
notp->srcp(aselp);
|
|
|
|
|
}
|
|
|
|
|
term.m_vtxp->replaceWith(notp);
|
|
|
|
|
} else {
|
|
|
|
|
term.m_vtxp->replaceWith(aselp);
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(term.m_vtxp->unlinkDelete(dfg), term.m_vtxp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 21:37:01 +02:00
|
|
|
void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) {
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
// There is absolutely nothing useful we can do with a graph of size 2 or less
|
|
|
|
|
if (dfg.size() <= 2) return;
|
|
|
|
|
|
2025-09-10 13:38:49 +02:00
|
|
|
const auto run = [&](const std::string& name, bool dump, std::function<void()> pass) {
|
|
|
|
|
// Apply the pass
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
pass();
|
2025-09-10 13:38:49 +02:00
|
|
|
// Debug dump
|
|
|
|
|
if (dump) dfg.dumpDotFilePrefixed(ctx.prefix() + "opt-" + VString::removeWhitespace(name));
|
|
|
|
|
// Internal type check
|
2025-09-07 21:38:50 +02:00
|
|
|
if (v3Global.opt.debugCheck()) V3DfgPasses::typeCheck(dfg);
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
};
|
|
|
|
|
|
2025-09-10 13:38:49 +02:00
|
|
|
// Currend debug dump level
|
|
|
|
|
const uint32_t dumpLvl = dumpDfgLevel();
|
|
|
|
|
|
|
|
|
|
// Run passes
|
|
|
|
|
run("input ", dumpLvl >= 3, [&]() { /* debug dump only */ });
|
|
|
|
|
run("inlineVars ", dumpLvl >= 4, [&]() { inlineVars(dfg); });
|
|
|
|
|
run("cse0 ", dumpLvl >= 4, [&]() { cse(dfg, ctx.m_cseContext0); });
|
|
|
|
|
run("binToOneHot ", dumpLvl >= 4, [&]() { binToOneHot(dfg, ctx.m_binToOneHotContext); });
|
|
|
|
|
run("peephole ", dumpLvl >= 4, [&]() { peephole(dfg, ctx.m_peepholeContext); });
|
|
|
|
|
run("cse1 ", dumpLvl >= 4, [&]() { cse(dfg, ctx.m_cseContext1); });
|
|
|
|
|
run("output ", dumpLvl >= 3, [&]() { /* debug dump only */ });
|
|
|
|
|
|
2024-03-02 20:49:29 +01:00
|
|
|
// Accumulate patterns for reporting
|
|
|
|
|
if (v3Global.opt.stats()) ctx.m_patternStats.accumulate(dfg);
|
Introduce DFG based combinational logic optimizer (#3527)
Added a new data-flow graph (DFG) based combinational logic optimizer.
The capabilities of this covers a combination of V3Const and V3Gate, but
is also more capable of transforming combinational logic into simplified
forms and more.
This entail adding a new internal representation, `DfgGraph`, and
appropriate `astToDfg` and `dfgToAst` conversion functions. The graph
represents some of the combinational equations (~continuous assignments)
in a module, and for the duration of the DFG passes, it takes over the
role of AstModule. A bulk of the Dfg vertices represent expressions.
These vertex classes, and the corresponding conversions to/from AST are
mostly auto-generated by astgen, together with a DfgVVisitor that can be
used for dynamic dispatch based on vertex (operation) types.
The resulting combinational logic graph (a `DfgGraph`) is then optimized
in various ways. Currently we perform common sub-expression elimination,
variable inlining, and some specific peephole optimizations, but there
is scope for more optimizations in the future using the same
representation. The optimizer is run directly before and after inlining.
The pre inline pass can operate on smaller graphs and hence converges
faster, but still has a chance of substantially reducing the size of the
logic on some designs, making inlining both faster and less memory
intensive. The post inline pass can then optimize across the inlined
module boundaries. No optimization is performed across a module
boundary.
For debugging purposes, each peephole optimization can be disabled
individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one
of the optimizations listed in V3DfgPeephole.h, for example
-fno-dfg-peephole-remove-not-not.
The peephole patterns currently implemented were mostly picked based on
the design that inspired this work, and on that design the optimizations
yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As
you can imagine not having to haul around redundant combinational
networks in the rest of the compilation pipeline also helps with memory
consumption, and up to 30% peak memory usage of Verilator was observed
on the same design.
Gains on other arbitrary designs are smaller (and can be improved by
analyzing those designs). For example OpenTitan gains between 1-15%
speedup depending on build type.
2022-09-23 17:46:22 +02:00
|
|
|
}
|