From d6237e55b2480dfeb1c30a88bef2f87fcb1302a1 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 17 Jun 2021 17:27:45 +0100 Subject: [PATCH] Internals: Add AstUserNAllocator utility classes. These utility classes can be used to hang advanced data structures off AstNode user*u() pointers, and they take care of memory management for the client. Use via the call operator(). --- src/V3Ast.h | 13 +++-- src/V3AstUserAllocator.h | 119 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 src/V3AstUserAllocator.h diff --git a/src/V3Ast.h b/src/V3Ast.h index 1fd3940d3..c525af73d 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -26,6 +26,7 @@ #include "V3Global.h" #include +#include #include #include "V3Ast__gen_classes.h" // From ./astgen @@ -1141,10 +1142,14 @@ public: explicit VNUser(void* p) { m_u.up = p; } ~VNUser() = default; // Casters - WidthVP* c() const { return reinterpret_cast(m_u.up); } - VSymEnt* toSymEnt() const { return reinterpret_cast(m_u.up); } - AstNode* toNodep() const { return reinterpret_cast(m_u.up); } - V3GraphVertex* toGraphVertex() const { return reinterpret_cast(m_u.up); } + template // + typename std::enable_if::value, T>::type to() const { + return reinterpret_cast(m_u.up); + } + WidthVP* c() const { return to(); } + VSymEnt* toSymEnt() const { return to(); } + AstNode* toNodep() const { return to(); } + V3GraphVertex* toGraphVertex() const { return to(); } int toInt() const { return m_u.ui; } static VNUser fromInt(int i) { return VNUser(i); } }; diff --git a/src/V3AstUserAllocator.h b/src/V3AstUserAllocator.h new file mode 100644 index 000000000..7ffb70f7b --- /dev/null +++ b/src/V3AstUserAllocator.h @@ -0,0 +1,119 @@ +// -*- 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 +// +//************************************************************************* +// +// Copyright 2003-2021 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_V3ASTUSERALLOCATOR_H_ +#define VERILATOR_V3ASTUSERALLOCATOR_H_ + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Ast.h" + +#include +#include + +template class AstUserAllocatorBase VL_NOT_FINAL { + static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number"); + static_assert(std::is_base_of::value, "T_Node must be an AstNode type"); + +private: + std::vector m_allocated; + + inline T_Data* getUserp(T_Node* nodep) const { + // This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'. + if (T_UserN == 1) { + VNUser user = nodep->user1u(); + return user.to(); + } else if (T_UserN == 2) { + VNUser user = nodep->user2u(); + return user.to(); + } else if (T_UserN == 3) { + VNUser user = nodep->user3u(); + return user.to(); + } else if (T_UserN == 4) { + VNUser user = nodep->user4u(); + return user.to(); + } else { + VNUser user = nodep->user5u(); + return user.to(); + } + } + + 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) { + AstUser1InUse::check(); + } else if (T_UserN == 2) { + AstUser2InUse::check(); + } else if (T_UserN == 3) { + AstUser3InUse::check(); + } else if (T_UserN == 4) { + AstUser4InUse::check(); + } else { + AstUser5InUse::check(); + } + } + + virtual ~AstUserAllocatorBase() { + // Delete all allocated data structures + for (T_Data* const p : m_allocated) { delete p; } + } + +public: + // Get a reference to the user data + T_Data& operator()(T_Node* nodep) { + T_Data* userp = getUserp(nodep); + if (!userp) { + userp = new T_Data; + m_allocated.push_back(userp); + setUserp(nodep, userp); + } + return *userp; + } +}; + +// 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 AstUser1Allocator final : public AstUserAllocatorBase {}; +template +class AstUser2Allocator final : public AstUserAllocatorBase {}; +template +class AstUser3Allocator final : public AstUserAllocatorBase {}; +template +class AstUser4Allocator final : public AstUserAllocatorBase {}; +template +class AstUser5Allocator final : public AstUserAllocatorBase {}; + +#endif // Guard