Merge pull request #534 from antmicro/mglb/FixLeaks
Fix memory leaks detected by ASAN.
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index 36c5080..f1b81a9 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -13,6 +13,8 @@
#include "frontends/ast/ast.h"
#include "libs/sha1/sha1.h"
+#include "utils/memory.h"
+
// UHDM
#include <uhdm/ExprEval.h>
#include <uhdm/uhdm.h>
@@ -493,21 +495,16 @@
}
if (wiretype_node == nullptr)
return;
- std::vector<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> unpacked_ranges;
- // First check if it has already defined ranges
- if (wire_node->attributes.count(UhdmAst::packed_ranges())) {
- for (auto r : wire_node->attributes[UhdmAst::packed_ranges()]->children) {
- packed_ranges.push_back(r->clone());
- }
- }
- if (wire_node->attributes.count(UhdmAst::unpacked_ranges())) {
- for (auto r : wire_node->attributes[UhdmAst::unpacked_ranges()]->children) {
- unpacked_ranges.push_back(r->clone());
- }
- }
+
+ unique_resource<std::vector<AST::AstNode *>> packed_ranges = wire_node->attributes.count(attr_id::packed_ranges)
+ ? std::move(wire_node->attributes[attr_id::packed_ranges]->children)
+ : std::vector<AST::AstNode *>{};
delete_attribute(wire_node, attr_id::packed_ranges);
+ unique_resource<std::vector<AST::AstNode *>> unpacked_ranges = wire_node->attributes.count(attr_id::unpacked_ranges)
+ ? std::move(wire_node->attributes[attr_id::unpacked_ranges]->children)
+ : std::vector<AST::AstNode *>{};
delete_attribute(wire_node, attr_id::unpacked_ranges);
+
AST::AstNode *wiretype_ast = nullptr;
log_assert(AST_INTERNAL::current_scope.count(wiretype_node->str));
wiretype_ast = AST_INTERNAL::current_scope[wiretype_node->str];
@@ -555,10 +552,10 @@
// add wiretype range before current wire ranges
std::reverse(packed_ranges_wiretype.begin(), packed_ranges_wiretype.end());
std::reverse(unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end());
- std::reverse(packed_ranges.begin(), packed_ranges.end());
- std::reverse(unpacked_ranges.begin(), unpacked_ranges.end());
- packed_ranges.insert(packed_ranges.begin(), packed_ranges_wiretype.begin(), packed_ranges_wiretype.end());
- unpacked_ranges.insert(unpacked_ranges.begin(), unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end());
+ std::reverse(packed_ranges->begin(), packed_ranges->end());
+ std::reverse(unpacked_ranges->begin(), unpacked_ranges->end());
+ packed_ranges->insert(packed_ranges->begin(), packed_ranges_wiretype.begin(), packed_ranges_wiretype.end());
+ unpacked_ranges->insert(unpacked_ranges->begin(), unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end());
AST::AstNode *value = nullptr;
if (wire_node->children[0]->type != AST::AST_RANGE) {
value = wire_node->children[0]->clone();
@@ -566,7 +563,7 @@
delete_children(wire_node);
if (value)
wire_node->children.push_back(value);
- add_multirange_wire(wire_node, packed_ranges, unpacked_ranges, false /* reverse */);
+ add_multirange_wire(wire_node, packed_ranges.release(), unpacked_ranges.release(), false /* reverse */);
}
}
@@ -2271,7 +2268,7 @@
current_node = make_ast_node(AST::AST_CELL);
std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> parameters;
- std::vector<AST::AstNode *> parameter_typedefs;
+ auto parameter_typedefs = make_unique_resource<std::vector<AST::AstNode *>>();
visit_one_to_many({vpiParameter}, obj_h, [&](AST::AstNode *node) {
log_assert(node);
@@ -2300,7 +2297,7 @@
if (child->type == AST::AST_TYPEDEF || child->type == AST::AST_ENUM) {
// Copy definition of the type provided as parameter.
- parameter_typedefs.push_back(child->clone());
+ parameter_typedefs->push_back(child->clone());
}
}
delete node;
@@ -2366,7 +2363,9 @@
}
}
});
- module_node->children.insert(std::end(module_node->children), std::begin(parameter_typedefs), std::end(parameter_typedefs));
+ module_node->children.insert(std::end(module_node->children), std::begin(*parameter_typedefs), std::end(*parameter_typedefs));
+ parameter_typedefs->clear();
+ parameter_typedefs.reset();
if (module_node->attributes.count(UhdmAst::partial())) {
AST::AstNode *attr = module_node->attributes.at(UhdmAst::partial());
if (attr->type == AST::AST_CONSTANT)
@@ -3091,7 +3090,8 @@
auto top_node = find_ancestor({AST::AST_MODULE});
if (!top_node)
return;
- top_node->children.push_back(current_node->children[0]->clone());
+ top_node->children.push_back(std::move(current_node->children[0]));
+ delete current_node;
current_node = nullptr;
}
}
@@ -3974,6 +3974,7 @@
if (current_node->children.empty()) {
delete assign_node->children[0];
assign_node->children[0] = assignments[0]->children[0];
+ delete current_node;
current_node = assignments[0]->children[1];
assignments[0]->children.clear();
delete assignments[0];
diff --git a/systemverilog-plugin/utils/memory.h b/systemverilog-plugin/utils/memory.h
new file mode 100644
index 0000000..6cd43c0
--- /dev/null
+++ b/systemverilog-plugin/utils/memory.h
@@ -0,0 +1,195 @@
+#ifndef SYSTEMVERILOG_PLUGIN_UTILS_MEMORY_H_
+#define SYSTEMVERILOG_PLUGIN_UTILS_MEMORY_H_
+
+#include <cassert>
+#include <utility>
+
+namespace systemverilog_plugin
+{
+
+// `std::default_delete` equivalent for any range of pointers, e.g. `std::vector<Object *>`.
+template <class Range> struct default_delete_ptr_range {
+ void operator()(Range &range) const
+ {
+ for (auto *ptr : range)
+ delete ptr;
+ }
+};
+
+// Functor that takes a reference and does nothing. Useful as no-op deleter.
+struct noop_delete {
+ template <class T> void operator()(T &) const {}
+};
+
+namespace utils_memory_internal
+{
+
+// Unique type for detecting invalid (missing) default_resource_deleter.
+struct missing_type {
+};
+
+// Provider of default deleter functor for resource of type `R` used by `unique_resource`.
+template <class R, class AlwaysVoid_ = void> struct default_resource_deleter {
+ using type = missing_type;
+};
+
+// Type trait for detecting whether type `R` is any range of pointers.
+template <class R, class ValueType_ = std::remove_reference_t<decltype(*std::begin(std::declval<R>()))>>
+using is_range_of_pointers_t = std::enable_if_t<std::is_pointer_v<ValueType_> && !std::is_array_v<ValueType_>>;
+
+// Overload for any range of pointers.
+template <class R> struct default_resource_deleter<R, is_range_of_pointers_t<R>> {
+ using type = default_delete_ptr_range<R>;
+};
+
+// Convenience alias.
+template <class R> using default_resource_deleter_t = typename default_resource_deleter<R>::type;
+
+// Type trait for checking whether type `R` is a valid deleter of type `D`.
+template <class R, class D, class = void> struct is_valid_resource_deleter : std::false_type {
+};
+template <class R, class D> struct is_valid_resource_deleter<R, D, std::void_t<decltype(std::declval<D>()(std::declval<R &>()))>> : std::true_type {
+};
+
+// Convenience alias.
+template <class R, class D> inline constexpr bool is_valid_resource_deleter_v = is_valid_resource_deleter<R, D>::value;
+
+} // namespace utils_memory_internal
+
+// Wrapper that holds and manages resource of type `Resource`. Equivalent of `unique_ptr` for non-pointer types.
+//
+// `unique_resource` tracks initialization status of its resource. The `Destructor` is called only when the resource is in initialized state.
+// `unique_resource` constructed using default constructor is in uninitialized state. It becomes initialized when a valid resource is moved into it.
+// Moving resource out or releasing it switches state to uninitialized.
+//
+// The API is based on unique_ptr rather than `unique_resource` from Library Fundamentals TS3.
+template <class Resource, class Deleter = utils_memory_internal::default_resource_deleter_t<Resource>> class unique_resource
+{
+ // Check for errors in template parameters.
+ // Use of intermediate constexprs results in nicer error messages.
+ static constexpr bool deleter_for_resource_exists = !std::is_same_v<Deleter, utils_memory_internal::missing_type>;
+ static_assert(deleter_for_resource_exists, "'Deleter' has not been specified and no default deleter exists for type 'Resource'.");
+ static constexpr bool deleter_is_callable_with_resource_ref = utils_memory_internal::is_valid_resource_deleter_v<Resource, Deleter>;
+ static_assert(deleter_is_callable_with_resource_ref, "Object of type 'Deleter' must be callable with argument of type 'Resource &'.");
+ static constexpr bool resource_type_is_not_cvref = std::is_same_v<Resource, std::remove_cv_t<std::remove_reference_t<Resource>>>;
+ static_assert(resource_type_is_not_cvref, "'Resource' must not be a reference or have const or volatile qualifier.");
+
+ // Data members.
+
+ Resource resource = {};
+ bool initialized = false;
+
+ public:
+ // Initialize
+
+ unique_resource() = default;
+
+ template <class OtherResource> unique_resource(OtherResource &&other) : resource(std::forward<OtherResource>(other)), initialized(true) {}
+
+ // Copy
+
+ unique_resource(const unique_resource &) = delete;
+
+ unique_resource &operator=(const unique_resource &) = delete;
+
+ // Move
+
+ template <class OtherDeleter>
+ unique_resource(unique_resource<Resource, OtherDeleter> &&other) : resource(std::move(other.resource)), initialized(other.initialized)
+ {
+ other.initialized = false;
+ }
+
+ template <class OtherDeleter> unique_resource &operator=(unique_resource<Resource, OtherDeleter> &&other)
+ {
+ resource = std::move(other.resource);
+ initialized = other.initialized;
+ other.initialized = false;
+ };
+
+ // Destroy
+
+ ~unique_resource()
+ {
+ if (initialized) {
+ Deleter{}(resource);
+ }
+ }
+
+ // Data access
+
+ Resource &get()
+ {
+ assert(initialized);
+ return resource;
+ }
+
+ const Resource &get() const
+ {
+ assert(initialized);
+ return resource;
+ }
+
+ Resource &operator*()
+ {
+ assert(initialized);
+ return resource;
+ }
+
+ const Resource &operator*() const
+ {
+ assert(initialized);
+ return resource;
+ }
+
+ Resource *operator->()
+ {
+ assert(initialized);
+ return &resource;
+ }
+
+ const Resource *operator->() const
+ {
+ assert(initialized);
+ return &resource;
+ }
+
+ operator bool() const { return initialized; }
+
+ // Operations
+
+ Resource release()
+ {
+ Resource r = std::move(resource);
+ initialized = false;
+ return r;
+ }
+
+ void reset()
+ {
+ if (initialized) {
+ Deleter{}(resource);
+ initialized = false;
+ }
+ }
+
+ template <class OtherResource> void reset(OtherResource &&other)
+ {
+ if (initialized) {
+ Deleter{}(resource);
+ }
+ resource = std::forward<OtherResource>(other);
+ initialized = true;
+ }
+};
+
+// Creates `unique_resource<Resource, Deleter>` and initializes it with a resource constructed using specified arguments.
+template <class Resource, class Deleter = utils_memory_internal::default_resource_deleter_t<Resource>, class... Tn>
+inline unique_resource<Resource, Deleter> make_unique_resource(Tn &&... arg_n)
+{
+ return unique_resource<Resource, Deleter>(Resource(std::forward<Tn>(arg_n)...));
+}
+
+} // namespace systemverilog_plugin
+
+#endif // SYSTEMVERILOG_PLUGIN_UTILS_MEMORY_H_