include/PatriciaTreeMapAbstractPartition.h (170 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <ostream> #include <cstddef> #include <functional> #include <initializer_list> #include <ostream> #include <utility> #include "AbstractDomain.h" #include "PatriciaTreeMap.h" namespace sparta { /* * An abstract partition based on Patricia trees that is cheap to copy. * * In order to minimize the size of the underlying tree, we do not explicitly * represent bindings of a label to the Bottom element. * * See HashedAbstractPartition.h for more details about abstract partitions. * * This implementation differs slightly from the textbook definition of a * partition: our Top partition cannot have its labels re-bound to anything * other than Top. I.e. for all labels L and domains D, * * PatriciaTreeMapAbstractPartition::top().set(L, D) == * PatriciaTreeMapAbstractPartition::top() * * This makes for a much simpler implementation. */ template <typename Label, typename Domain> class PatriciaTreeMapAbstractPartition final : public AbstractDomain<PatriciaTreeMapAbstractPartition<Label, Domain>> { public: struct ValueInterface { using type = Domain; static type default_value() { return type::bottom(); } static bool is_default_value(const type& x) { return x.is_bottom(); } static bool equals(const type& x, const type& y) { return x.equals(y); } static bool leq(const type& x, const type& y) { return x.leq(y); } }; using MapType = PatriciaTreeMap<Label, Domain, ValueInterface>; /* * The default constructor produces the Bottom value. */ PatriciaTreeMapAbstractPartition() = default; PatriciaTreeMapAbstractPartition( std::initializer_list<std::pair<Label, Domain>> l) { for (const auto& p : l) { set(p.first, p.second); } } /* * Number of bindings not set to Bottom. This operation is not defined if the * PatriciaTreeMapAbstractPartition is set to Top. */ size_t size() const { RUNTIME_CHECK(!is_top(), undefined_operation()); return m_map.size(); } /* * Get the bindings that are not set to Bottom. This operation is not defined * if the PatriciaTreeMapAbstractPartition is set to Top. */ const MapType& bindings() const { RUNTIME_CHECK(!is_top(), undefined_operation()); return m_map; } const Domain& get(const Label& label) const { if (is_top()) { static const Domain top = Domain::top(); return top; } return m_map.at(label); } /* * This is a no-op if the partition is set to Top. */ PatriciaTreeMapAbstractPartition& set(const Label& label, const Domain& value) { if (is_top()) { return *this; } m_map.insert_or_assign(label, value); return *this; } /* * This is a no-op if the partition is set to Top. */ PatriciaTreeMapAbstractPartition& update( const Label& label, std::function<Domain(const Domain&)> operation) { if (is_top()) { return *this; } m_map.update(operation, label); return *this; } bool map(std::function<Domain(const Domain&)> f) { if (is_top()) { return false; } return m_map.map(f); } bool is_top() const override { return m_is_top; } bool is_bottom() const override { return !m_is_top && m_map.empty(); } void set_to_bottom() override { m_map.clear(); m_is_top = false; } void set_to_top() override { m_map.clear(); m_is_top = true; } bool leq(const PatriciaTreeMapAbstractPartition& other) const override { if (is_top()) { return other.is_top(); } if (other.is_top()) { return true; } return m_map.leq(other.m_map); } bool equals(const PatriciaTreeMapAbstractPartition& other) const override { if (m_is_top != other.m_is_top) { return false; } return m_map.equals(other.m_map); } void join_with(const PatriciaTreeMapAbstractPartition& other) override { join_like_operation( other, [](const Domain& x, const Domain& y) { return x.join(y); }); } void widen_with(const PatriciaTreeMapAbstractPartition& other) override { join_like_operation( other, [](const Domain& x, const Domain& y) { return x.widening(y); }); } void meet_with(const PatriciaTreeMapAbstractPartition& other) override { meet_like_operation( other, [](const Domain& x, const Domain& y) { return x.meet(y); }); } void narrow_with(const PatriciaTreeMapAbstractPartition& other) override { meet_like_operation( other, [](const Domain& x, const Domain& y) { return x.narrowing(y); }); } void join_like_operation( const PatriciaTreeMapAbstractPartition& other, std::function<Domain(const Domain&, const Domain&)> operation) { if (is_top()) { return; } if (other.is_top()) { set_to_top(); return; } m_map.union_with(operation, other.m_map); } void meet_like_operation( const PatriciaTreeMapAbstractPartition& other, std::function<Domain(const Domain&, const Domain&)> operation) { if (is_top()) { *this = other; return; } if (other.is_top()) { return; } m_map.intersection_with(operation, other.m_map); } void difference_like_operation( const PatriciaTreeMapAbstractPartition& other, std::function<Domain(const Domain&, const Domain&)> operation) { if (other.is_top()) { set_to_bottom(); } else if (is_top()) { return; } else { m_map.difference_with(operation, other.m_map); } } static PatriciaTreeMapAbstractPartition bottom() { return PatriciaTreeMapAbstractPartition(); } static PatriciaTreeMapAbstractPartition top() { auto part = PatriciaTreeMapAbstractPartition(); part.m_is_top = true; return part; } private: MapType m_map; bool m_is_top{false}; }; } // namespace sparta template <typename Label, typename Domain> inline std::ostream& operator<<( std::ostream& o, const typename sparta::PatriciaTreeMapAbstractPartition<Label, Domain>& partition) { if (partition.is_bottom()) { o << "_|_"; } else if (partition.is_top()) { o << "T"; } else { o << "[#" << partition.size() << "]"; o << partition.bindings(); } return o; }