2021-06-17 16:27:45 +00:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
//*************************************************************************
|
|
|
|
// DESCRIPTION: Verilator: Utility to hang advanced data structures of
|
|
|
|
// AstNode::user*p() pointers with automatic memory management.
|
|
|
|
//
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
2022-01-01 13:26:40 +00:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2021-06-17 16:27:45 +00: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
|
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
#ifndef VERILATOR_V3ASTUSERALLOCATOR_H_
|
|
|
|
#define VERILATOR_V3ASTUSERALLOCATOR_H_
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
|
|
#include <type_traits>
|
2021-12-17 17:56:33 +00:00
|
|
|
#include <utility>
|
2021-06-17 16:27:45 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
|
|
|
|
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
|
|
|
|
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<T_Data*> m_allocated;
|
|
|
|
|
Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-06-29 16:57:07 +00:00
|
|
|
inline T_Data* getUserp(const T_Node* nodep) const {
|
2021-06-17 16:27:45 +00:00
|
|
|
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
|
|
|
if (T_UserN == 1) {
|
2021-06-20 22:32:57 +00:00
|
|
|
const VNUser user = nodep->user1u();
|
2021-06-17 16:27:45 +00:00
|
|
|
return user.to<T_Data*>();
|
|
|
|
} else if (T_UserN == 2) {
|
2021-06-20 22:32:57 +00:00
|
|
|
const VNUser user = nodep->user2u();
|
2021-06-17 16:27:45 +00:00
|
|
|
return user.to<T_Data*>();
|
|
|
|
} else if (T_UserN == 3) {
|
2021-06-20 22:32:57 +00:00
|
|
|
const VNUser user = nodep->user3u();
|
2021-06-17 16:27:45 +00:00
|
|
|
return user.to<T_Data*>();
|
|
|
|
} else if (T_UserN == 4) {
|
2021-06-20 22:32:57 +00:00
|
|
|
const VNUser user = nodep->user4u();
|
2021-06-17 16:27:45 +00:00
|
|
|
return user.to<T_Data*>();
|
|
|
|
} else {
|
2021-06-20 22:32:57 +00:00
|
|
|
const VNUser user = nodep->user5u();
|
2021-06-17 16:27:45 +00:00
|
|
|
return user.to<T_Data*>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void setUserp(T_Node* nodep, T_Data* userp) const {
|
|
|
|
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
|
|
|
if (T_UserN == 1) {
|
|
|
|
nodep->user1u(VNUser(userp));
|
|
|
|
} else if (T_UserN == 2) {
|
|
|
|
nodep->user2u(VNUser(userp));
|
|
|
|
} else if (T_UserN == 3) {
|
|
|
|
nodep->user3u(VNUser(userp));
|
|
|
|
} else if (T_UserN == 4) {
|
|
|
|
nodep->user4u(VNUser(userp));
|
|
|
|
} else {
|
|
|
|
nodep->user5u(VNUser(userp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
AstUserAllocatorBase() {
|
|
|
|
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
|
|
|
if (T_UserN == 1) {
|
2022-01-02 18:56:40 +00:00
|
|
|
VNUser1InUse::check();
|
2021-06-17 16:27:45 +00:00
|
|
|
} else if (T_UserN == 2) {
|
2022-01-02 18:56:40 +00:00
|
|
|
VNUser2InUse::check();
|
2021-06-17 16:27:45 +00:00
|
|
|
} else if (T_UserN == 3) {
|
2022-01-02 18:56:40 +00:00
|
|
|
VNUser3InUse::check();
|
2021-06-17 16:27:45 +00:00
|
|
|
} else if (T_UserN == 4) {
|
2022-01-02 18:56:40 +00:00
|
|
|
VNUser4InUse::check();
|
2021-06-17 16:27:45 +00:00
|
|
|
} else {
|
2022-01-02 18:56:40 +00:00
|
|
|
VNUser5InUse::check();
|
2021-06-17 16:27:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~AstUserAllocatorBase() {
|
|
|
|
// Delete all allocated data structures
|
|
|
|
for (T_Data* const p : m_allocated) { delete p; }
|
|
|
|
}
|
|
|
|
|
2021-12-22 11:41:29 +00:00
|
|
|
VL_UNCOPYABLE(AstUserAllocatorBase);
|
|
|
|
|
2021-06-17 16:27:45 +00:00
|
|
|
public:
|
2021-12-17 17:56:33 +00:00
|
|
|
// Get a reference to the user data. If does not exist, construct it with given arguments.
|
|
|
|
template <typename... Args> //
|
|
|
|
T_Data& operator()(T_Node* nodep, Args&&... args) {
|
2021-06-17 16:27:45 +00:00
|
|
|
T_Data* userp = getUserp(nodep);
|
|
|
|
if (!userp) {
|
2021-12-17 17:56:33 +00:00
|
|
|
userp = new T_Data{std::forward<Args>(args)...};
|
2021-06-17 16:27:45 +00:00
|
|
|
m_allocated.push_back(userp);
|
|
|
|
setUserp(nodep, userp);
|
|
|
|
}
|
|
|
|
return *userp;
|
|
|
|
}
|
Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-06-29 16:57:07 +00:00
|
|
|
|
|
|
|
// Get a reference to the user data
|
|
|
|
T_Data& operator()(const T_Node* nodep) {
|
2021-11-26 22:55:36 +00:00
|
|
|
T_Data* const userp = getUserp(nodep);
|
Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-06-29 16:57:07 +00:00
|
|
|
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
|
|
|
|
return *userp;
|
|
|
|
}
|
2021-12-17 17:56:33 +00:00
|
|
|
|
|
|
|
// Get a pointer to the user data if exists, otherwise nullptr
|
|
|
|
T_Data* tryGet(const T_Node* nodep) { return getUserp(nodep); }
|
2021-06-17 16:27:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
|
|
|
|
// and is simply there for a bit of extra type safety. T_Data is the type of the data structure
|
|
|
|
// managed by the allocator.
|
|
|
|
template <class T_Node, class T_Data>
|
|
|
|
class AstUser1Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 1> {};
|
|
|
|
template <class T_Node, class T_Data>
|
|
|
|
class AstUser2Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 2> {};
|
|
|
|
template <class T_Node, class T_Data>
|
|
|
|
class AstUser3Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 3> {};
|
|
|
|
template <class T_Node, class T_Data>
|
|
|
|
class AstUser4Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 4> {};
|
|
|
|
template <class T_Node, class T_Data>
|
|
|
|
class AstUser5Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 5> {};
|
|
|
|
|
|
|
|
#endif // Guard
|