verilator/src/V3List.h

144 lines
5.0 KiB
C
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: List class with storage in existing classes
//
2019-11-08 03:33:59 +00:00
// Code available from: https://verilator.org
//
//*************************************************************************
//
2023-01-01 15:18:39 +00:00
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3LIST_H_
#define VERILATOR_V3LIST_H_
#include "config_build.h"
#include "verilatedos.h"
#include <vector>
//============================================================================
template <class T>
class V3List;
template <class T>
class V3ListEnt;
template <class T>
class V3List final {
// List container for linked list of elements of type *T (T is a pointer type)
private:
// MEMBERS
T m_headp = nullptr; // First element
T m_tailp = nullptr; // Last element
friend class V3ListEnt<T>;
public:
V3List() = default;
~V3List() = default;
// METHODS
T begin() const { return m_headp; }
T end() const { return nullptr; }
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 15:46:22 +00:00
T rbegin() const { return m_tailp; }
T rend() const { return nullptr; }
bool empty() const { return m_headp == nullptr; }
void reset() { // clear() without walking the list
m_headp = nullptr;
m_tailp = nullptr;
}
};
//============================================================================
template <class T>
class V3ListEnt final {
// List entry for linked list of elements of type *T (T is a pointer type)
private:
// MEMBERS
T m_nextp = nullptr; // Pointer to next element, nullptr=end
T m_prevp = nullptr; // Pointer to previous element, nullptr=beginning
friend class V3List<T>;
static V3ListEnt* baseToListEnt(void* newbasep, size_t offset) {
2022-12-10 02:06:27 +00:00
// "this" must be an element inside of *basep
// Use that to determine a structure offset, then apply to the new base
// to get our new pointer information
return (V3ListEnt*)(((uint8_t*)newbasep) + offset);
}
public:
V3ListEnt() = default;
~V3ListEnt() {
#ifdef VL_DEBUG
// Load bogus pointers so we can catch deletion bugs
m_nextp = reinterpret_cast<T>(1);
m_prevp = reinterpret_cast<T>(1);
#endif
}
T nextp() const { return m_nextp; }
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 15:46:22 +00:00
T prevp() const { return m_prevp; }
// METHODS
void pushBack(V3List<T>& listr, T newp) {
2022-12-10 02:06:27 +00:00
// "this" must be an element inside of *newp
// cppcheck-suppress thisSubtraction
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp);
m_nextp = nullptr;
if (!listr.m_headp) listr.m_headp = newp;
m_prevp = listr.m_tailp;
if (m_prevp) baseToListEnt(m_prevp, offset)->m_nextp = newp;
listr.m_tailp = newp;
}
void pushFront(V3List<T>& listr, T newp) {
2022-12-10 02:06:27 +00:00
// "this" must be an element inside of *newp
// cppcheck-suppress thisSubtraction
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp);
m_nextp = listr.m_headp;
if (m_nextp) baseToListEnt(m_nextp, offset)->m_prevp = newp;
listr.m_headp = newp;
m_prevp = nullptr;
if (!listr.m_tailp) listr.m_tailp = newp;
}
// Unlink from side
void unlink(V3List<T>& listr, T oldp) {
2022-12-10 02:06:27 +00:00
// "this" must be an element inside of *oldp
// cppcheck-suppress thisSubtraction
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(oldp);
if (m_nextp) {
baseToListEnt(m_nextp, offset)->m_prevp = m_prevp;
} else {
listr.m_tailp = m_prevp;
}
if (m_prevp) {
baseToListEnt(m_prevp, offset)->m_nextp = m_nextp;
} else {
listr.m_headp = m_nextp;
}
m_prevp = m_nextp = nullptr;
}
// Remove all nodes from 'oldListr', append them to 'newListr'. 'this' must be a member of the
// object at 'selfp', and 'selfp' must be the head of the list in 'oldListr'.
void moveAppend(V3List<T>& oldListr, V3List<T>& newListr, T selfp) {
UASSERT(selfp == oldListr.m_headp, "Must be head of list to use 'moveAppend'");
const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(selfp);
const T headp = selfp;
const T tailp = oldListr.m_tailp;
oldListr.reset();
if (newListr.empty()) {
newListr.m_headp = headp;
newListr.m_tailp = tailp;
} else {
baseToListEnt(newListr.m_tailp, offset)->m_nextp = headp;
m_prevp = newListr.m_tailp;
newListr.m_tailp = tailp;
}
}
};
//============================================================================
#endif // Guard