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: Convert AstModule to 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-11-11 05:25:53 +01:00
|
|
|
// Convert and AstModule to a DfgGraph. We proceed by visiting convertible logic blocks (e.g.:
|
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
|
|
|
// AstAssignW of appropriate type and with no delays), recursively constructing DfgVertex instances
|
|
|
|
|
// for the expressions that compose the subject logic block. If all expressions in the current
|
|
|
|
|
// logic block can be converted, then we delete the logic block (now represented in the DfgGraph),
|
|
|
|
|
// and connect the corresponding DfgVertex instances appropriately. If some of the expressions were
|
|
|
|
|
// not convertible in the current logic block, we revert (delete) the DfgVertex instances created
|
|
|
|
|
// for the logic block, and leave the logic block in the AstModule. Any variable reference from
|
|
|
|
|
// non-converted logic blocks (or other constructs under the AstModule) are marked as being
|
|
|
|
|
// referenced in the AstModule, which is relevant for later optimization.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
#include "V3Const.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 "V3Dfg.h"
|
|
|
|
|
#include "V3DfgPasses.h"
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
#include <iterator>
|
|
|
|
|
|
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
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
// Create a DfgVertex out of a AstNodeExpr. For most AstNodeExpr subtypes, this can be done
|
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
|
|
|
// automatically. For the few special cases, we provide specializations below
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_Vertex, typename T_Node>
|
|
|
|
|
T_Vertex* makeVertex(const T_Node* nodep, DfgGraph& dfg) {
|
|
|
|
|
return new T_Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
|
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-07-21 18:33:12 +02:00
|
|
|
template <>
|
|
|
|
|
DfgArraySel* makeVertex<DfgArraySel, AstArraySel>(const AstArraySel* nodep, DfgGraph& dfg) {
|
|
|
|
|
// Some earlier passes create malformed ArraySels, just bail on those...
|
|
|
|
|
// See t_bitsel_wire_array_bad
|
|
|
|
|
if (VN_IS(nodep->fromp(), Const)) return nullptr;
|
|
|
|
|
AstUnpackArrayDType* const fromDtypep
|
|
|
|
|
= VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType);
|
|
|
|
|
if (!fromDtypep) return nullptr;
|
|
|
|
|
return new DfgArraySel{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
//======================================================================
|
|
|
|
|
// Currently unhandled nodes
|
|
|
|
|
// LCOV_EXCL_START
|
|
|
|
|
// AstCCast changes width, but should not exists where DFG optimization is currently invoked
|
|
|
|
|
template <>
|
2022-10-04 12:03:41 +02:00
|
|
|
DfgCCast* makeVertex<DfgCCast, AstCCast>(const AstCCast*, DfgGraph&) {
|
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
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway
|
|
|
|
|
template <>
|
2022-10-04 12:03:41 +02:00
|
|
|
DfgAtoN* makeVertex<DfgAtoN, AstAtoN>(const AstAtoN*, DfgGraph&) {
|
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
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// Unhandled in DfgToAst, but also operates on strings which we don't optimize anyway
|
|
|
|
|
template <>
|
2022-10-04 12:03:41 +02:00
|
|
|
DfgCompareNN* makeVertex<DfgCompareNN, AstCompareNN>(const AstCompareNN*, DfgGraph&) {
|
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
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// Unhandled in DfgToAst, but also operates on unpacked arrays which we don't optimize anyway
|
|
|
|
|
template <>
|
2022-10-04 12:03:41 +02:00
|
|
|
DfgSliceSel* makeVertex<DfgSliceSel, AstSliceSel>(const AstSliceSel*, DfgGraph&) {
|
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
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// LCOV_EXCL_STOP
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2025-07-01 23:55:08 +02:00
|
|
|
template <bool T_Scoped>
|
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
|
|
|
class AstToDfgVisitor final : public VNVisitor {
|
|
|
|
|
// NODE STATE
|
|
|
|
|
|
|
|
|
|
// AstNode::user1p // DfgVertex for this AstNode
|
|
|
|
|
const VNUser1InUse m_user1InUse;
|
|
|
|
|
|
2022-11-12 15:14:32 +01:00
|
|
|
// TYPES
|
2025-07-01 23:55:08 +02:00
|
|
|
using RootType = std::conditional_t<T_Scoped, AstNetlist, AstModule>;
|
|
|
|
|
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
|
|
|
|
|
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
|
|
|
// STATE
|
|
|
|
|
|
|
|
|
|
DfgGraph* const m_dfgp; // The graph being built
|
|
|
|
|
V3DfgOptimizationContext& m_ctx; // The optimization context for stats
|
|
|
|
|
bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit'
|
|
|
|
|
std::vector<DfgVertex*> m_uncommittedVertices; // Vertices that we might decide to revert
|
2022-09-26 15:21:05 +02:00
|
|
|
bool m_converting = false; // We are trying to convert some logic at the moment
|
2022-09-27 01:06:50 +02:00
|
|
|
std::vector<DfgVarPacked*> m_varPackedps; // All the DfgVarPacked vertices we created.
|
|
|
|
|
std::vector<DfgVarArray*> m_varArrayps; // All the DfgVarArray vertices we created.
|
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
|
|
|
|
|
|
|
|
// METHODS
|
2025-07-01 23:55:08 +02:00
|
|
|
static VariableType* getTarget(const AstVarRef* refp) {
|
|
|
|
|
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
|
|
|
|
|
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
|
|
|
|
return reinterpret_cast<VariableType*>(refp->varScopep());
|
|
|
|
|
} else {
|
|
|
|
|
return reinterpret_cast<VariableType*>(refp->varp());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static AstVar* getAstVar(VariableType* vp) {
|
|
|
|
|
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
|
|
|
|
|
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
|
|
|
|
return reinterpret_cast<AstVarScope*>(vp)->varp();
|
|
|
|
|
} else {
|
|
|
|
|
return reinterpret_cast<AstVar*>(vp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 markReferenced(AstNode* nodep) {
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->foreach([this](const AstVarRef* refp) {
|
2025-07-21 19:32:08 +02:00
|
|
|
// No need to (and in fact cannot) mark variables if:
|
|
|
|
|
if (!DfgVertex::isSupportedDType(refp->varp()->dtypep())) return; // unsupported type
|
|
|
|
|
if (refp->varp()->isSc()) return; // SystemC
|
2025-07-01 23:55:08 +02:00
|
|
|
VariableType* const tgtp = getTarget(refp);
|
2022-11-12 12:35:23 +01:00
|
|
|
// Mark vertex as having a module reference outside current DFG
|
2025-07-01 23:55:08 +02:00
|
|
|
getNet(tgtp)->setHasModRefs();
|
2022-11-12 12:35:23 +01:00
|
|
|
// Mark variable as written from non-DFG logic
|
2025-07-01 23:55:08 +02:00
|
|
|
if (refp->access().isWriteOrRW()) tgtp->user3(true);
|
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 commitVertices() { m_uncommittedVertices.clear(); }
|
|
|
|
|
|
|
|
|
|
void revertUncommittedVertices() {
|
|
|
|
|
for (DfgVertex* const vtxp : m_uncommittedVertices) vtxp->unlinkDelete(*m_dfgp);
|
|
|
|
|
m_uncommittedVertices.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 23:55:08 +02:00
|
|
|
DfgVertexVar* getNet(VariableType* vp) {
|
|
|
|
|
if (!vp->user1p()) {
|
|
|
|
|
// vp DfgVertexVar vertices are not added to m_uncommittedVertices, because we
|
2022-09-27 01:06:50 +02:00
|
|
|
// want to hold onto them via AstVar::user1p, and the AstVar might be referenced via
|
2022-10-04 12:03:41 +02:00
|
|
|
// multiple AstVarRef instances, so we will never revert a DfgVertexVar once
|
2022-09-30 12:35:03 +02:00
|
|
|
// created. We will delete unconnected variable vertices at the end.
|
2025-07-01 23:55:08 +02:00
|
|
|
if (VN_IS(vp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
|
|
|
|
DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, vp};
|
|
|
|
|
vp->user1p();
|
2022-09-27 01:06:50 +02:00
|
|
|
m_varArrayps.push_back(vtxp);
|
2025-07-01 23:55:08 +02:00
|
|
|
vp->user1p(vtxp);
|
2022-09-27 01:06:50 +02:00
|
|
|
} else {
|
2025-07-01 23:55:08 +02:00
|
|
|
DfgVarPacked* const vtxp = new DfgVarPacked{*m_dfgp, vp};
|
2022-09-27 01:06:50 +02:00
|
|
|
m_varPackedps.push_back(vtxp);
|
2025-07-01 23:55:08 +02:00
|
|
|
vp->user1p(vtxp);
|
2022-09-27 01:06:50 +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-07-01 23:55:08 +02:00
|
|
|
return vp->user1u().template to<DfgVertexVar*>();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DfgVertex* getVertex(AstNode* nodep) {
|
|
|
|
|
DfgVertex* vtxp = nodep->user1u().to<DfgVertex*>();
|
|
|
|
|
UASSERT_OBJ(vtxp, nodep, "Missing Dfg vertex");
|
|
|
|
|
return vtxp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true if the expression cannot (or should not) be represented by DFG
|
2022-10-12 11:19:21 +02:00
|
|
|
bool unhandled(AstNodeExpr* nodep) {
|
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
|
|
|
// Short-circuiting if something was already unhandled
|
|
|
|
|
if (!m_foundUnhandled) {
|
|
|
|
|
// Impure nodes cannot be represented
|
|
|
|
|
if (!nodep->isPure()) {
|
|
|
|
|
m_foundUnhandled = true;
|
|
|
|
|
++m_ctx.m_nonRepImpure;
|
|
|
|
|
}
|
|
|
|
|
// Check node has supported dtype
|
|
|
|
|
if (!DfgVertex::isSupportedDType(nodep->dtypep())) {
|
|
|
|
|
m_foundUnhandled = true;
|
|
|
|
|
++m_ctx.m_nonRepDType;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return m_foundUnhandled;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
std::pair<DfgVertexSplice*, uint32_t> convertLValue(AstNode* nodep) {
|
2022-09-25 17:03:15 +02:00
|
|
|
if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
|
|
|
|
m_foundUnhandled = false;
|
|
|
|
|
visit(vrefp);
|
2025-07-21 18:33:12 +02:00
|
|
|
if (m_foundUnhandled) return {nullptr, 0};
|
|
|
|
|
|
|
|
|
|
// Get the variable vertex
|
2025-07-14 23:09:34 +02:00
|
|
|
DfgVertexVar* const vtxp = getVertex(vrefp)->template as<DfgVertexVar>();
|
2025-07-21 18:33:12 +02:00
|
|
|
// Ensure the Splice driver exists for this variable
|
2025-07-14 23:09:34 +02:00
|
|
|
if (!vtxp->srcp()) {
|
2025-07-21 18:33:12 +02:00
|
|
|
FileLine* const flp = vtxp->fileline();
|
|
|
|
|
AstNodeDType* const dtypep = vtxp->dtypep();
|
|
|
|
|
if (vtxp->is<DfgVarPacked>()) {
|
|
|
|
|
vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, dtypep});
|
|
|
|
|
} else if (vtxp->is<DfgVarArray>()) {
|
|
|
|
|
vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, dtypep});
|
2025-07-14 23:09:34 +02:00
|
|
|
} else {
|
2025-07-21 18:33:12 +02:00
|
|
|
nodep->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE
|
2025-07-14 23:09:34 +02:00
|
|
|
}
|
2022-09-27 01:06:50 +02:00
|
|
|
}
|
2025-07-21 18:33:12 +02:00
|
|
|
// Return the Splice driver
|
|
|
|
|
return {vtxp->srcp()->as<DfgVertexSplice>(), 0};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AstSel* selp = VN_CAST(nodep, Sel)) {
|
|
|
|
|
// Only handle constant selects
|
|
|
|
|
const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const);
|
|
|
|
|
if (!lsbp) {
|
|
|
|
|
++m_ctx.m_nonRepLhs;
|
|
|
|
|
return {nullptr, 0};
|
|
|
|
|
}
|
|
|
|
|
uint32_t lsb = lsbp->toUInt();
|
|
|
|
|
|
|
|
|
|
// Convert the 'fromp' sub-expression
|
|
|
|
|
const auto pair = convertLValue(selp->fromp());
|
|
|
|
|
if (!pair.first) return {nullptr, 0};
|
|
|
|
|
DfgSplicePacked* const splicep = pair.first->template as<DfgSplicePacked>();
|
|
|
|
|
// Adjust index.
|
|
|
|
|
lsb += pair.second;
|
|
|
|
|
|
|
|
|
|
// AstSel doesn't change type kind (array vs packed), so we can use
|
|
|
|
|
// the existing splice driver with adjusted lsb
|
|
|
|
|
return {splicep, lsb};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AstArraySel* const aselp = VN_CAST(nodep, ArraySel)) {
|
|
|
|
|
// Only handle constant selects
|
|
|
|
|
const AstConst* const indexp = VN_CAST(aselp->bitp(), Const);
|
|
|
|
|
if (!indexp) {
|
|
|
|
|
++m_ctx.m_nonRepLhs;
|
|
|
|
|
return {nullptr, 0};
|
|
|
|
|
}
|
|
|
|
|
uint32_t index = indexp->toUInt();
|
|
|
|
|
|
|
|
|
|
// Convert the 'fromp' sub-expression
|
|
|
|
|
const auto pair = convertLValue(aselp->fromp());
|
|
|
|
|
if (!pair.first) return {nullptr, 0};
|
|
|
|
|
DfgSpliceArray* const splicep = pair.first->template as<DfgSpliceArray>();
|
|
|
|
|
// Adjust index. Note pair.second is always 0, but we might handle array slices later..
|
|
|
|
|
index += pair.second;
|
|
|
|
|
|
|
|
|
|
// Ensure the Splice driver exists for this element
|
|
|
|
|
if (!splicep->driverAt(index)) {
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstNodeDType* const dtypep = DfgVertex::dtypeFor(nodep);
|
|
|
|
|
if (VN_IS(dtypep, BasicDType)) {
|
|
|
|
|
splicep->addDriver(flp, index, new DfgSplicePacked{*m_dfgp, flp, dtypep});
|
|
|
|
|
} else if (VN_IS(dtypep, UnpackArrayDType)) {
|
|
|
|
|
splicep->addDriver(flp, index, new DfgSpliceArray{*m_dfgp, flp, dtypep});
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Unhandled AstNodeDType sub-type"); // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the splice driver
|
|
|
|
|
return {splicep->driverAt(index)->as<DfgVertexSplice>(), 0};
|
2022-09-25 17:03:15 +02:00
|
|
|
}
|
2025-07-14 23:09:34 +02:00
|
|
|
|
|
|
|
|
++m_ctx.m_nonRepLhs;
|
2025-07-21 18:33:12 +02:00
|
|
|
return {nullptr, 0};
|
2025-07-14 23:09:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build DfgEdge representing the LValue assignment. Returns false if unsuccessful.
|
2025-07-21 18:33:12 +02:00
|
|
|
bool convertAssignment(FileLine* flp, AstNode* lhsp, DfgVertex* vtxp) {
|
|
|
|
|
// Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _)
|
|
|
|
|
lhsp = V3Const::constifyExpensiveEdit(lhsp);
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
// Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part
|
2025-07-21 18:33:12 +02:00
|
|
|
if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) {
|
|
|
|
|
AstNode* const cLhsp = concatp->lhsp();
|
|
|
|
|
AstNode* const cRhsp = concatp->rhsp();
|
2022-09-25 17:03:15 +02:00
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
{ // Convet LHS of concat
|
2025-07-21 18:33:12 +02:00
|
|
|
FileLine* const lFlp = cLhsp->fileline();
|
|
|
|
|
DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(cLhsp)};
|
2022-09-25 17:03:15 +02:00
|
|
|
lVtxp->fromp(vtxp);
|
2025-07-21 18:33:12 +02:00
|
|
|
lVtxp->lsb(cRhsp->width());
|
|
|
|
|
if (!convertAssignment(flp, cLhsp, lVtxp)) return false;
|
2022-09-25 17:03:15 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
{ // Convert RHS of concat
|
2025-07-21 18:33:12 +02:00
|
|
|
FileLine* const rFlp = cRhsp->fileline();
|
|
|
|
|
DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(cRhsp)};
|
2022-09-25 17:03:15 +02:00
|
|
|
rVtxp->fromp(vtxp);
|
2022-10-06 19:34:18 +02:00
|
|
|
rVtxp->lsb(0);
|
2025-07-21 18:33:12 +02:00
|
|
|
return convertAssignment(flp, cRhsp, rVtxp);
|
2022-09-25 17:03:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-14 23:09:34 +02:00
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Construct LHS
|
|
|
|
|
const auto pair = convertLValue(lhsp);
|
|
|
|
|
if (!pair.first) return false;
|
|
|
|
|
|
|
|
|
|
// If successful connect the driver
|
|
|
|
|
if (DfgSplicePacked* const sPackedp = pair.first->template cast<DfgSplicePacked>()) {
|
|
|
|
|
sPackedp->addDriver(flp, pair.second, vtxp);
|
|
|
|
|
} else if (DfgSpliceArray* const sArrayp = pair.first->template cast<DfgSpliceArray>()) {
|
|
|
|
|
sArrayp->addDriver(flp, pair.second, vtxp);
|
2025-07-14 23:09:34 +02:00
|
|
|
} else {
|
2025-07-21 18:33:12 +02:00
|
|
|
lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE
|
2025-07-14 23:09:34 +02:00
|
|
|
}
|
2025-07-21 18:33:12 +02:00
|
|
|
|
|
|
|
|
return true;
|
2022-09-25 17:03:15 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-01 13:28:16 +02:00
|
|
|
bool convertEquation(AstNode* nodep, FileLine* flp, AstNode* lhsp, AstNode* rhsp) {
|
2022-09-25 17:03:15 +02:00
|
|
|
UASSERT_OBJ(m_uncommittedVertices.empty(), nodep, "Should not nest");
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Check data types are compatible.
|
|
|
|
|
if (!DfgVertex::isSupportedDType(lhsp->dtypep())
|
|
|
|
|
|| !DfgVertex::isSupportedDType(rhsp->dtypep())) {
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
++m_ctx.m_nonRepDType;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c)
|
|
|
|
|
if (VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType) && !VN_IS(rhsp, VarRef)) {
|
2022-09-27 01:06:50 +02:00
|
|
|
markReferenced(nodep);
|
|
|
|
|
++m_ctx.m_nonRepDType;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-25 17:03:15 +02:00
|
|
|
// Cannot handle mismatched widths. Mismatched assignments should have been fixed up in
|
|
|
|
|
// earlier passes anyway, so this should never be hit, but being paranoid just in case.
|
|
|
|
|
if (lhsp->width() != rhsp->width()) { // LCOV_EXCL_START
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
++m_ctx.m_nonRepWidth;
|
|
|
|
|
return false;
|
|
|
|
|
} // LCOV_EXCL_STOP
|
|
|
|
|
|
|
|
|
|
VL_RESTORER(m_converting);
|
|
|
|
|
m_converting = true;
|
|
|
|
|
|
|
|
|
|
m_foundUnhandled = false;
|
|
|
|
|
iterate(rhsp);
|
2025-06-28 18:29:41 +02:00
|
|
|
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
2022-09-25 17:03:15 +02:00
|
|
|
if (m_foundUnhandled) {
|
|
|
|
|
revertUncommittedVertices();
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 13:28:16 +02:00
|
|
|
if (!convertAssignment(flp, lhsp, getVertex(rhsp))) {
|
2022-09-25 17:03:15 +02:00
|
|
|
revertUncommittedVertices();
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connect the rhs vertex to the driven edge
|
|
|
|
|
commitVertices();
|
|
|
|
|
|
|
|
|
|
// Remove node from Ast. Now represented by the Dfg.
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
++m_ctx.m_representable;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Prune vertices potentially unused due to resolving multiple drivers.
|
|
|
|
|
// Having multiple drivers is an error and is hence assumed to be rare,
|
|
|
|
|
// so performance is not very important, set will suffice.
|
|
|
|
|
void removeUnused(std::set<DfgVertex*>& prune) {
|
|
|
|
|
while (!prune.empty()) {
|
|
|
|
|
// Pop last vertex
|
|
|
|
|
const auto it = prune.begin();
|
|
|
|
|
DfgVertex* const vtxp = *it;
|
|
|
|
|
prune.erase(it);
|
|
|
|
|
// If used (or a variable), then done
|
|
|
|
|
if (vtxp->hasSinks() || vtxp->is<DfgVertexVar>()) continue;
|
|
|
|
|
// If unused, then add sources to work list and delete
|
|
|
|
|
vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); });
|
|
|
|
|
vtxp->unlinkDelete(*m_dfgp);
|
2022-11-12 15:14:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Normalize packed driver - return the normalized vertex and location for 'splicep'
|
|
|
|
|
std::pair<DfgVertex*, FileLine*> //
|
|
|
|
|
normalizePacked(DfgVertexVar* varp, const std::string& sub, DfgSplicePacked* const splicep) {
|
|
|
|
|
// Represents a driver of 'splicep'
|
|
|
|
|
struct Driver final {
|
|
|
|
|
FileLine* m_fileline;
|
|
|
|
|
DfgVertex* m_vtxp;
|
|
|
|
|
uint32_t m_lsb;
|
|
|
|
|
Driver() = delete;
|
|
|
|
|
Driver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp)
|
|
|
|
|
: m_fileline{flp}
|
|
|
|
|
, m_vtxp{vtxp}
|
|
|
|
|
, m_lsb{lsb} {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// The drivers of 'splicep'
|
|
|
|
|
std::vector<Driver> drivers;
|
|
|
|
|
drivers.reserve(splicep->arity());
|
|
|
|
|
|
|
|
|
|
// Sometime assignment ranges are coalesced by V3Const,
|
|
|
|
|
// so we unpack concatenations for better error reporting.
|
|
|
|
|
const std::function<void(FileLine*, uint32_t, DfgVertex*)> gather
|
|
|
|
|
= [&](FileLine* flp, uint32_t lsb, DfgVertex* vtxp) -> void {
|
|
|
|
|
if (DfgConcat* const concatp = vtxp->cast<DfgConcat>()) {
|
|
|
|
|
DfgVertex* const rhsp = concatp->rhsp();
|
|
|
|
|
auto const rhs_width = rhsp->width();
|
|
|
|
|
gather(rhsp->fileline(), lsb, rhsp);
|
|
|
|
|
DfgVertex* const lhsp = concatp->lhsp();
|
|
|
|
|
gather(lhsp->fileline(), lsb + rhs_width, lhsp);
|
|
|
|
|
concatp->unlinkDelete(*m_dfgp);
|
|
|
|
|
} else {
|
|
|
|
|
drivers.emplace_back(flp, lsb, vtxp);
|
2022-11-12 15:14:32 +01:00
|
|
|
}
|
2025-07-21 18:33:12 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Gather and unlink all drivers
|
|
|
|
|
splicep->forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
|
|
|
|
|
DfgVertex* const driverp = edge.sourcep();
|
|
|
|
|
UASSERT(driverp, "Should not have created undriven sources");
|
|
|
|
|
UASSERT_OBJ(!driverp->is<DfgVertexSplice>(), splicep, "Should not be DfgVertexSplice");
|
|
|
|
|
gather(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp);
|
|
|
|
|
edge.unlinkSource();
|
|
|
|
|
});
|
2022-09-27 01:06:50 +02:00
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
const auto cmp = [](const Driver& a, const Driver& b) {
|
|
|
|
|
if (a.m_lsb != b.m_lsb) return a.m_lsb < b.m_lsb;
|
|
|
|
|
return a.m_fileline->operatorCompare(*b.m_fileline) < 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sort drivers by LSB
|
|
|
|
|
std::stable_sort(drivers.begin(), drivers.end(), cmp);
|
|
|
|
|
|
|
|
|
|
// Vertices that might have become unused due to multiple driver resolution. Having
|
|
|
|
|
// multiple drivers is an error and is hence assumed to be rare, so performance is
|
|
|
|
|
// not very important, set will suffice.
|
|
|
|
|
std::set<DfgVertex*> prune;
|
|
|
|
|
|
|
|
|
|
// Fix multiply driven ranges
|
|
|
|
|
for (auto it = drivers.begin(); it != drivers.end();) {
|
|
|
|
|
Driver& a = *it++;
|
|
|
|
|
const uint32_t aWidth = a.m_vtxp->width();
|
|
|
|
|
const uint32_t aEnd = a.m_lsb + aWidth;
|
|
|
|
|
while (it != drivers.end()) {
|
|
|
|
|
Driver& b = *it;
|
|
|
|
|
// If no overlap, then nothing to do
|
|
|
|
|
if (b.m_lsb >= aEnd) break;
|
2022-09-27 01:06:50 +02:00
|
|
|
|
2022-11-12 15:14:32 +01:00
|
|
|
const uint32_t bWidth = b.m_vtxp->width();
|
2025-07-21 18:33:12 +02:00
|
|
|
const uint32_t bEnd = b.m_lsb + bWidth;
|
|
|
|
|
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
|
|
|
|
|
|
|
|
|
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0
|
|
|
|
|
&& !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress
|
|
|
|
|
) {
|
|
|
|
|
AstNode* const vp = varp->varScopep()
|
|
|
|
|
? static_cast<AstNode*>(varp->varScopep())
|
|
|
|
|
: static_cast<AstNode*>(varp->varp());
|
|
|
|
|
|
|
|
|
|
vp->v3warn( //
|
|
|
|
|
MULTIDRIVEN,
|
|
|
|
|
"Bits [" //
|
|
|
|
|
<< overlapEnd << ":" << b.m_lsb << "] of signal '" << vp->prettyName()
|
|
|
|
|
<< sub << "' have multiple combinational drivers\n"
|
|
|
|
|
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
|
|
|
|
<< a.m_fileline->warnContextPrimary() << '\n'
|
|
|
|
|
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
|
|
|
|
<< b.m_fileline->warnContextSecondary() << vp->warnOther()
|
|
|
|
|
<< "... Only the first driver will be respected");
|
2022-09-27 01:06:50 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// If the first driver completely covers the range of the second driver,
|
|
|
|
|
// we can just delete the second driver completely, otherwise adjust the
|
|
|
|
|
// second driver to apply from the end of the range of the first driver.
|
|
|
|
|
if (aEnd >= bEnd) {
|
|
|
|
|
prune.emplace(b.m_vtxp);
|
|
|
|
|
it = drivers.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd);
|
|
|
|
|
DfgSel* const selp = new DfgSel{*m_dfgp, b.m_vtxp->fileline(), dtypep};
|
|
|
|
|
selp->fromp(b.m_vtxp);
|
|
|
|
|
selp->lsb(aEnd - b.m_lsb);
|
|
|
|
|
b.m_lsb = aEnd;
|
|
|
|
|
b.m_vtxp = selp;
|
|
|
|
|
std::stable_sort(it, drivers.end(), cmp);
|
2022-09-27 01:06:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-21 18:33:12 +02:00
|
|
|
}
|
2022-09-27 01:06:50 +02:00
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Coalesce adjacent ranges
|
|
|
|
|
for (size_t i = 0, j = 1; j < drivers.size(); ++j) {
|
|
|
|
|
Driver& a = drivers[i];
|
|
|
|
|
Driver& b = drivers[j];
|
|
|
|
|
|
|
|
|
|
// Coalesce adjacent range
|
|
|
|
|
const uint32_t aWidth = a.m_vtxp->width();
|
|
|
|
|
const uint32_t bWidth = b.m_vtxp->width();
|
|
|
|
|
if (a.m_lsb + aWidth == b.m_lsb) {
|
|
|
|
|
const auto dtypep = DfgVertex::dtypeForWidth(aWidth + bWidth);
|
|
|
|
|
DfgConcat* const concatp = new DfgConcat{*m_dfgp, a.m_fileline, dtypep};
|
|
|
|
|
concatp->rhsp(a.m_vtxp);
|
|
|
|
|
concatp->lhsp(b.m_vtxp);
|
|
|
|
|
a.m_vtxp = concatp;
|
|
|
|
|
b.m_vtxp = nullptr; // Mark as moved
|
|
|
|
|
++m_ctx.m_coalescedAssignments;
|
|
|
|
|
continue;
|
2022-11-12 15:14:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
++i;
|
2025-07-14 23:09:34 +02:00
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Compact non-adjacent ranges within the vector
|
|
|
|
|
if (j != i) {
|
|
|
|
|
Driver& c = drivers[i];
|
|
|
|
|
UASSERT_OBJ(!c.m_vtxp, c.m_fileline, "Should have been marked moved");
|
|
|
|
|
c = b;
|
|
|
|
|
b.m_vtxp = nullptr; // Mark as moved
|
2025-07-14 23:09:34 +02:00
|
|
|
}
|
2022-09-27 01:06:50 +02:00
|
|
|
}
|
2025-07-21 18:33:12 +02:00
|
|
|
|
|
|
|
|
// Reinsert drivers in order
|
|
|
|
|
splicep->resetSources();
|
|
|
|
|
for (const Driver& driver : drivers) {
|
|
|
|
|
if (!driver.m_vtxp) break; // Stop at end of compacted list
|
|
|
|
|
splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeUnused(prune);
|
|
|
|
|
|
|
|
|
|
// If the whole variable is driven whole, we can just use that driver
|
|
|
|
|
if (splicep->arity() == 1 //
|
|
|
|
|
&& splicep->driverLsb(0) == 0 //
|
|
|
|
|
&& splicep->source(0)->width() == splicep->width()) {
|
|
|
|
|
const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0));
|
|
|
|
|
VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return {splicep, splicep->fileline()};
|
2022-09-27 01:06:50 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
// Normalize array driver - return the normalized vertex and location for 'splicep'
|
|
|
|
|
std::pair<DfgVertex*, FileLine*> //
|
|
|
|
|
normalizeArray(DfgVertexVar* varp, const std::string& sub, DfgSpliceArray* const splicep) {
|
|
|
|
|
// Represents a driver of 'splicep'
|
|
|
|
|
struct Driver final {
|
|
|
|
|
FileLine* m_fileline;
|
|
|
|
|
DfgVertex* m_vtxp;
|
|
|
|
|
uint32_t m_idx;
|
|
|
|
|
Driver() = delete;
|
|
|
|
|
Driver(FileLine* flp, uint32_t idx, DfgVertex* vtxp)
|
|
|
|
|
: m_fileline{flp}
|
|
|
|
|
, m_vtxp{vtxp}
|
|
|
|
|
, m_idx{idx} {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// The drivers of 'splicep'
|
|
|
|
|
std::vector<Driver> drivers;
|
|
|
|
|
drivers.reserve(splicep->arity());
|
|
|
|
|
|
|
|
|
|
// Normalize, gather, and unlink all drivers
|
|
|
|
|
splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) {
|
|
|
|
|
DfgVertex* const driverp = edge.sourcep();
|
|
|
|
|
UASSERT(driverp, "Should not have created undriven sources");
|
|
|
|
|
const uint32_t idx = splicep->driverIndex(i);
|
|
|
|
|
if (DfgSplicePacked* const spp = driverp->cast<DfgSplicePacked>()) {
|
|
|
|
|
const auto pair
|
|
|
|
|
= normalizePacked(varp, sub + "[" + std::to_string(idx) + "]", spp);
|
|
|
|
|
drivers.emplace_back(pair.second, idx, pair.first);
|
|
|
|
|
} else if (DfgSpliceArray* const sap = driverp->cast<DfgSpliceArray>()) {
|
|
|
|
|
const auto pair = normalizeArray(varp, sub + "[" + std::to_string(idx) + "]", sap);
|
|
|
|
|
drivers.emplace_back(pair.second, idx, pair.first);
|
|
|
|
|
} else if (driverp->is<DfgVertexSplice>()) {
|
|
|
|
|
driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type");
|
|
|
|
|
} else {
|
|
|
|
|
drivers.emplace_back(splicep->driverFileLine(i), idx, driverp);
|
|
|
|
|
}
|
|
|
|
|
edge.unlinkSource();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const auto cmp = [](const Driver& a, const Driver& b) {
|
|
|
|
|
if (a.m_idx != b.m_idx) return a.m_idx < b.m_idx;
|
|
|
|
|
return a.m_fileline->operatorCompare(*b.m_fileline) < 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sort drivers by index
|
|
|
|
|
std::stable_sort(drivers.begin(), drivers.end(), cmp);
|
|
|
|
|
|
|
|
|
|
// Vertices that become unused due to multiple driver resolution
|
|
|
|
|
std::set<DfgVertex*> prune;
|
|
|
|
|
|
|
|
|
|
// Fix multiply driven ranges
|
|
|
|
|
for (auto it = drivers.begin(); it != drivers.end();) {
|
|
|
|
|
Driver& a = *it++;
|
|
|
|
|
AstUnpackArrayDType* aArrayDTypep = VN_CAST(a.m_vtxp->dtypep(), UnpackArrayDType);
|
|
|
|
|
const uint32_t aElements = aArrayDTypep ? aArrayDTypep->elementsConst() : 1;
|
|
|
|
|
const uint32_t aEnd = a.m_idx + aElements;
|
|
|
|
|
while (it != drivers.end()) {
|
|
|
|
|
Driver& b = *it;
|
|
|
|
|
// If no overlap, then nothing to do
|
|
|
|
|
if (b.m_idx >= aEnd) break;
|
|
|
|
|
|
|
|
|
|
AstUnpackArrayDType* bArrayDTypep = VN_CAST(b.m_vtxp->dtypep(), UnpackArrayDType);
|
|
|
|
|
const uint32_t bElements = bArrayDTypep ? bArrayDTypep->elementsConst() : 1;
|
|
|
|
|
const uint32_t bEnd = b.m_idx + bElements;
|
|
|
|
|
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
|
|
|
|
|
|
|
|
|
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) {
|
|
|
|
|
AstNode* const vp = varp->varScopep()
|
|
|
|
|
? static_cast<AstNode*>(varp->varScopep())
|
|
|
|
|
: static_cast<AstNode*>(varp->varp());
|
|
|
|
|
|
|
|
|
|
vp->v3warn( //
|
|
|
|
|
MULTIDRIVEN,
|
|
|
|
|
"Elements [" //
|
|
|
|
|
<< overlapEnd << ":" << b.m_idx << "] of signal '" << vp->prettyName()
|
|
|
|
|
<< sub << "' have multiple combinational drivers\n"
|
|
|
|
|
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
|
|
|
|
<< a.m_fileline->warnContextPrimary() << '\n'
|
|
|
|
|
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
|
|
|
|
<< b.m_fileline->warnContextSecondary() << vp->warnOther()
|
|
|
|
|
<< "... Only the first driver will be respected");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the first driver completely covers the range of the second driver,
|
|
|
|
|
// we can just delete the second driver completely, otherwise adjust the
|
|
|
|
|
// second driver to apply from the end of the range of the first driver.
|
|
|
|
|
if (aEnd >= bEnd) {
|
|
|
|
|
prune.emplace(b.m_vtxp);
|
|
|
|
|
it = drivers.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
const auto distance = std::distance(drivers.begin(), it);
|
|
|
|
|
DfgVertex* const bVtxp = b.m_vtxp;
|
|
|
|
|
FileLine* const flp = b.m_vtxp->fileline();
|
|
|
|
|
AstNodeDType* const elemDtypep = DfgVertex::dtypeFor(
|
|
|
|
|
VN_AS(splicep->dtypep(), UnpackArrayDType)->subDTypep());
|
|
|
|
|
// Remove this driver
|
|
|
|
|
it = drivers.erase(it);
|
|
|
|
|
// Add missing items element-wise
|
|
|
|
|
for (uint32_t i = aEnd; i < bEnd; ++i) {
|
|
|
|
|
DfgArraySel* const aselp = new DfgArraySel{*m_dfgp, flp, elemDtypep};
|
|
|
|
|
aselp->fromp(bVtxp);
|
|
|
|
|
aselp->bitp(new DfgConst{*m_dfgp, flp, 32, i});
|
|
|
|
|
drivers.emplace_back(flp, i, aselp);
|
|
|
|
|
}
|
|
|
|
|
it = drivers.begin();
|
|
|
|
|
std::advance(it, distance);
|
|
|
|
|
std::stable_sort(it, drivers.end(), cmp);
|
|
|
|
|
}
|
2022-09-30 12:35:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-21 18:33:12 +02:00
|
|
|
|
|
|
|
|
// Reinsert drivers in order
|
|
|
|
|
splicep->resetSources();
|
|
|
|
|
for (const Driver& driver : drivers) {
|
|
|
|
|
if (!driver.m_vtxp) break; // Stop at end of compacted list
|
|
|
|
|
splicep->addDriver(driver.m_fileline, driver.m_idx, driver.m_vtxp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeUnused(prune);
|
|
|
|
|
|
|
|
|
|
// If the whole variable is driven whole, we can just use that driver
|
|
|
|
|
if (splicep->arity() == 1 //
|
|
|
|
|
&& splicep->driverIndex(0) == 0 //
|
|
|
|
|
&& splicep->source(0)->dtypep()->isSame(splicep->dtypep())) {
|
|
|
|
|
const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0));
|
|
|
|
|
VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return {splicep, splicep->fileline()};
|
2022-09-30 12:35:03 +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
|
|
|
// VISITORS
|
|
|
|
|
void visit(AstNode* nodep) override {
|
|
|
|
|
// Conservatively treat this node as unhandled
|
2022-09-26 15:21:05 +02:00
|
|
|
if (!m_foundUnhandled && m_converting) ++m_ctx.m_nonRepUnknown;
|
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
|
|
|
m_foundUnhandled = true;
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
}
|
2025-07-01 23:55:08 +02:00
|
|
|
|
|
|
|
|
void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); }
|
|
|
|
|
void visit(AstModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); }
|
|
|
|
|
void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); }
|
|
|
|
|
void visit(AstScope* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstActive* nodep) override {
|
|
|
|
|
if (nodep->hasCombo()) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 visit(AstCell* nodep) override { markReferenced(nodep); }
|
|
|
|
|
void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); }
|
2025-07-01 23:55:08 +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 visit(AstVar* nodep) override {
|
2025-07-01 23:55:08 +02:00
|
|
|
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
if (nodep->isSc()) return;
|
|
|
|
|
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
|
|
|
|
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
|
|
|
|
|
|
|
|
|
// Mark variables with external references
|
|
|
|
|
if (nodep->isIO() // Ports
|
|
|
|
|
|| nodep->user2() // Target of a hierarchical reference
|
|
|
|
|
|| nodep->isForced() // Forced
|
|
|
|
|
) {
|
|
|
|
|
getNet(reinterpret_cast<VariableType*>(nodep))->setHasExtRefs();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstVarScope* nodep) override {
|
|
|
|
|
if VL_CONSTEXPR_CXX17 (!T_Scoped) {
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
if (nodep->varp()->isSc()) return;
|
|
|
|
|
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
|
|
|
|
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
|
|
|
|
|
|
|
|
|
// Mark variables with external references
|
|
|
|
|
if (nodep->varp()->isIO() // Ports
|
|
|
|
|
|| nodep->user2() // Target of a hierarchical reference
|
|
|
|
|
|| nodep->varp()->isForced() // Forced
|
|
|
|
|
) {
|
|
|
|
|
getNet(reinterpret_cast<VariableType*>(nodep))->setHasExtRefs();
|
|
|
|
|
}
|
2024-03-03 17:22:41 +01: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 visit(AstAssignW* nodep) override {
|
2022-09-26 15:21:05 +02:00
|
|
|
++m_ctx.m_inputEquations;
|
|
|
|
|
|
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
|
|
|
// Cannot handle assignment with timing control yet
|
|
|
|
|
if (nodep->timingControlp()) {
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
++m_ctx.m_nonRepTiming;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 13:28:16 +02:00
|
|
|
convertEquation(nodep, nodep->fileline(), nodep->lhsp(), nodep->rhsp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstAlways* nodep) override {
|
|
|
|
|
// Ignore sequential logic, or if there are multiple statements
|
|
|
|
|
const VAlwaysKwd kwd = nodep->keyword();
|
|
|
|
|
if (nodep->sensesp() || !nodep->isJustOneBodyStmt()
|
|
|
|
|
|| (kwd != VAlwaysKwd::ALWAYS && kwd != VAlwaysKwd::ALWAYS_COMB)) {
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode* const stmtp = nodep->stmtsp();
|
|
|
|
|
|
|
|
|
|
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
|
|
|
|
++m_ctx.m_inputEquations;
|
|
|
|
|
if (assignp->timingControlp()) {
|
|
|
|
|
markReferenced(stmtp);
|
|
|
|
|
++m_ctx.m_nonRepTiming;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
convertEquation(nodep, assignp->fileline(), assignp->lhsp(), assignp->rhsp());
|
|
|
|
|
} else if (AstIf* const ifp = VN_CAST(stmtp, If)) {
|
|
|
|
|
// Will only handle single assignments to the same LHS in both branches
|
|
|
|
|
AstAssign* const thenp = VN_CAST(ifp->thensp(), Assign);
|
|
|
|
|
AstAssign* const elsep = VN_CAST(ifp->elsesp(), Assign);
|
|
|
|
|
if (!thenp || !elsep || thenp->nextp() || elsep->nextp()
|
|
|
|
|
|| !thenp->lhsp()->sameTree(elsep->lhsp())) {
|
|
|
|
|
markReferenced(stmtp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++m_ctx.m_inputEquations;
|
|
|
|
|
if (thenp->timingControlp() || elsep->timingControlp()) {
|
|
|
|
|
markReferenced(stmtp);
|
|
|
|
|
++m_ctx.m_nonRepTiming;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a conditional for the rhs by borrowing the components from the AstIf
|
|
|
|
|
AstCond* const rhsp = new AstCond{ifp->fileline(), //
|
|
|
|
|
ifp->condp()->unlinkFrBack(), //
|
|
|
|
|
thenp->rhsp()->unlinkFrBack(), //
|
|
|
|
|
elsep->rhsp()->unlinkFrBack()};
|
|
|
|
|
|
|
|
|
|
if (!convertEquation(nodep, ifp->fileline(), thenp->lhsp(), rhsp)) {
|
|
|
|
|
// Failed to convert. Mark 'rhsp', as 'convertEquation' only marks 'nodep'.
|
|
|
|
|
markReferenced(rhsp);
|
|
|
|
|
// Put the AstIf back together
|
|
|
|
|
ifp->condp(rhsp->condp()->unlinkFrBack());
|
|
|
|
|
thenp->rhsp(rhsp->thenp()->unlinkFrBack());
|
|
|
|
|
elsep->rhsp(rhsp->elsep()->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
// Delete the auxiliary conditional
|
|
|
|
|
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
|
|
|
|
} else {
|
|
|
|
|
markReferenced(stmtp);
|
|
|
|
|
}
|
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 visit(AstVarRef* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");
|
|
|
|
|
if (unhandled(nodep)) return;
|
|
|
|
|
|
|
|
|
|
if (nodep->access().isRW() // Cannot represent read-write references
|
|
|
|
|
|| nodep->varp()->isIfaceRef() // Cannot handle interface references
|
|
|
|
|
|| nodep->varp()->delayp() // Cannot handle delayed variables
|
|
|
|
|
|| nodep->classOrPackagep() // Cannot represent cross module references
|
2025-07-21 19:32:08 +02:00
|
|
|
|| nodep->varp()->isSc() // SystemC variables are special and rare, we can ignore
|
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
|
|
|
) {
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
m_foundUnhandled = true;
|
|
|
|
|
++m_ctx.m_nonRepVarRef;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 23:55:08 +02:00
|
|
|
// If the referenced variable is not in a regular module, then do not
|
|
|
|
|
// convert it. This is especially needed for variabels in interfaces
|
|
|
|
|
// which might be referenced via virtual intefaces, which cannot be
|
|
|
|
|
// resovled statically.
|
|
|
|
|
if (T_Scoped && !VN_IS(nodep->varScopep()->scopep()->modp(), Module)) {
|
|
|
|
|
markReferenced(nodep);
|
|
|
|
|
m_foundUnhandled = true;
|
|
|
|
|
++m_ctx.m_nonRepVarRef;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Sadly sometimes AstVarRef does not have the same dtype as the referenced variable
|
|
|
|
|
if (!DfgVertex::isSupportedDType(nodep->varp()->dtypep())) {
|
|
|
|
|
m_foundUnhandled = true;
|
|
|
|
|
++m_ctx.m_nonRepVarRef;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 23:55:08 +02:00
|
|
|
nodep->user1p(getNet(getTarget(nodep)));
|
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 visit(AstConst* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");
|
|
|
|
|
if (unhandled(nodep)) return;
|
2022-10-07 16:44:14 +02:00
|
|
|
DfgVertex* const vtxp = new DfgConst{*m_dfgp, nodep->fileline(), nodep->num()};
|
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
|
|
|
m_uncommittedVertices.push_back(vtxp);
|
|
|
|
|
nodep->user1p(vtxp);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 19:34:18 +02:00
|
|
|
void visit(AstSel* nodep) override {
|
|
|
|
|
UASSERT_OBJ(!nodep->user1p(), nodep, "Already has Dfg vertex");
|
|
|
|
|
if (unhandled(nodep)) return;
|
2025-06-24 17:59:09 +02:00
|
|
|
|
2022-10-06 19:34:18 +02:00
|
|
|
iterate(nodep->fromp());
|
|
|
|
|
if (m_foundUnhandled) return;
|
|
|
|
|
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
DfgVertex* vtxp = nullptr;
|
|
|
|
|
if (AstConst* const constp = VN_CAST(nodep->lsbp(), Const)) {
|
|
|
|
|
DfgSel* const selp = new DfgSel{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)};
|
|
|
|
|
selp->fromp(nodep->fromp()->user1u().to<DfgVertex*>());
|
|
|
|
|
selp->lsb(constp->toUInt());
|
|
|
|
|
vtxp = selp;
|
|
|
|
|
} else {
|
|
|
|
|
iterate(nodep->lsbp());
|
|
|
|
|
if (m_foundUnhandled) return;
|
|
|
|
|
DfgMux* const muxp = new DfgMux{*m_dfgp, flp, DfgVertex::dtypeFor(nodep)};
|
|
|
|
|
muxp->fromp(nodep->fromp()->user1u().to<DfgVertex*>());
|
|
|
|
|
muxp->lsbp(nodep->lsbp()->user1u().to<DfgVertex*>());
|
|
|
|
|
vtxp = muxp;
|
|
|
|
|
}
|
|
|
|
|
m_uncommittedVertices.push_back(vtxp);
|
|
|
|
|
nodep->user1p(vtxp);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-27 01:06:50 +02:00
|
|
|
// The rest of the 'visit' methods are generated by 'astgen'
|
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 "V3Dfg__gen_ast_to_dfg.h"
|
|
|
|
|
|
2025-07-01 23:55:08 +02:00
|
|
|
static DfgGraph* makeDfg(RootType& root) {
|
|
|
|
|
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
|
|
|
|
return new DfgGraph{nullptr, "netlist"};
|
|
|
|
|
} else {
|
|
|
|
|
AstModule* const modp = VN_AS((AstNode*)&(root), Module); // Remove this when C++17
|
|
|
|
|
return new DfgGraph{modp, modp->name()};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// CONSTRUCTOR
|
2025-07-01 23:55:08 +02:00
|
|
|
explicit AstToDfgVisitor(RootType& root, V3DfgOptimizationContext& ctx)
|
|
|
|
|
: m_dfgp{makeDfg(root)}
|
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
|
|
|
, m_ctx{ctx} {
|
|
|
|
|
// Build the DFG
|
2025-07-01 23:55:08 +02:00
|
|
|
iterate(&root);
|
|
|
|
|
UASSERT_OBJ(m_uncommittedVertices.empty(), &root, "Uncommitted vertices remain");
|
2022-09-25 17:03:15 +02:00
|
|
|
|
2025-07-21 18:33:12 +02:00
|
|
|
if (dumpDfgLevel() >= 8) m_dfgp->dumpDotFilePrefixed(ctx.prefix() + "ast2dfg");
|
|
|
|
|
|
|
|
|
|
// Normalize variable drivers (remove multiple drivers, remove unnecessary splice vertices)
|
|
|
|
|
for (DfgVertexVar* const varp : m_dfgp->varVertices().unlinkable()) {
|
|
|
|
|
// Delete variables with no sinks nor sources (this can happen due to reverting
|
|
|
|
|
// uncommitted vertices, which does not remove variables)
|
|
|
|
|
if (!varp->hasSinks() && !varp->srcp()) {
|
|
|
|
|
VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Nothing to do for un-driven (input) variables
|
|
|
|
|
if (!varp->srcp()) continue;
|
|
|
|
|
|
|
|
|
|
// The driver of a variable must always be a splice vertex, normalize it
|
|
|
|
|
std::pair<DfgVertex*, FileLine*> normalizedDriver;
|
|
|
|
|
if (DfgSpliceArray* const sArrayp = varp->srcp()->cast<DfgSpliceArray>()) {
|
|
|
|
|
normalizedDriver = normalizeArray(varp, "", sArrayp);
|
|
|
|
|
} else if (DfgSplicePacked* const sPackedp = varp->srcp()->cast<DfgSplicePacked>()) {
|
|
|
|
|
normalizedDriver = normalizePacked(varp, "", sPackedp);
|
|
|
|
|
} else {
|
|
|
|
|
varp->v3fatalSrc("Unhandled DfgSplicePacked sub-type"); // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
varp->srcp(normalizedDriver.first);
|
|
|
|
|
varp->driverFileLine(normalizedDriver.second);
|
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2025-07-01 23:55:08 +02:00
|
|
|
static DfgGraph* apply(RootType& root, V3DfgOptimizationContext& ctx) {
|
|
|
|
|
return AstToDfgVisitor{root, ctx}.m_dfgp;
|
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
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DfgGraph* V3DfgPasses::astToDfg(AstModule& module, V3DfgOptimizationContext& ctx) {
|
2025-07-01 23:55:08 +02:00
|
|
|
return AstToDfgVisitor</* T_Scoped: */ false>::apply(module, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DfgGraph* V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgOptimizationContext& ctx) {
|
|
|
|
|
return AstToDfgVisitor</* T_Scoped: */ true>::apply(netlist, 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
|
|
|
}
|