diff --git a/sparta/include/sparta/OverUnderSetAbstractDomain.h b/sparta/include/sparta/OverUnderSetAbstractDomain.h new file mode 100644 index 00000000000..5d3b89a0f33 --- /dev/null +++ b/sparta/include/sparta/OverUnderSetAbstractDomain.h @@ -0,0 +1,257 @@ +/* + * 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 + +#include +#include +#include + +namespace sparta { + +namespace over_under_set_impl { + +template +class OverUnderSetValue; + +} // namespace over_under_set_impl + +/** + * An implementation of powerset abstract domains that computes both an over- + * and under-approximation using the given set. + */ +template +class OverUnderSetAbstractDomain final + : public AbstractDomainScaffolding< + over_under_set_impl::OverUnderSetValue, + OverUnderSetAbstractDomain> { + public: + using Value = over_under_set_impl::OverUnderSetValue; + using Element = typename Set::value_type; + + /* Return the empty over-under set. */ + OverUnderSetAbstractDomain() { this->set_to_value(Value()); } + + explicit OverUnderSetAbstractDomain(AbstractValueKind kind) + : AbstractDomainScaffolding, + OverUnderSetAbstractDomain>(kind) {} + + explicit OverUnderSetAbstractDomain(Element e) { + this->set_to_value(Value(std::move(e))); + } + + explicit OverUnderSetAbstractDomain(std::initializer_list l) { + this->set_to_value(Value(l)); + } + + explicit OverUnderSetAbstractDomain(Set set) { + this->set_to_value(Value(std::move(set))); + } + + explicit OverUnderSetAbstractDomain(Set over, Set under) { + this->set_to_value(Value(std::move(over), std::move(under))); + } + + static OverUnderSetAbstractDomain bottom() { + return OverUnderSetAbstractDomain(AbstractValueKind::Bottom); + } + + static OverUnderSetAbstractDomain top() { + return OverUnderSetAbstractDomain(AbstractValueKind::Top); + } + + bool empty() const { return this->is_value() && this->get_value()->empty(); } + + const Set& over() const { + RUNTIME_CHECK(this->kind() == AbstractValueKind::Value, + invalid_abstract_value() + << expected_kind(AbstractValueKind::Value) + << actual_kind(this->kind())); + return this->get_value()->over(); + } + + const Set& under() const { + RUNTIME_CHECK(this->kind() == AbstractValueKind::Value, + invalid_abstract_value() + << expected_kind(AbstractValueKind::Value) + << actual_kind(this->kind())); + return this->get_value()->under(); + } + + void add_over(const Element& e) { add_over_internal(e); } + + void add_over(const Set& set) { add_over_internal(set); } + + void add_over(Set&& set) { add_over_internal(std::move(set)); } + + void add_under(const Element& e) { add_under_internal(e); } + + void add_under(const Set& set) { add_under_internal(set); } + + void add_under(Set&& set) { add_under_internal(std::move(set)); } + + void add(const OverUnderSetAbstractDomain& other) { + if (this->is_top() || other.is_bottom()) { + return; + } else if (other.is_top()) { + this->set_to_top(); + } else if (this->is_bottom()) { + this->set_to_value(*other.get_value()); + } else { + this->get_value()->add(*other.get_value()); + } + } + + friend std::ostream& operator<<(std::ostream& out, + const OverUnderSetAbstractDomain& x) { + using namespace sparta; + switch (x.kind()) { + case AbstractValueKind::Bottom: { + out << "_|_"; + break; + } + case AbstractValueKind::Top: { + out << "T"; + break; + } + case AbstractValueKind::Value: { + out << *x.get_value(); + break; + } + } + return out; + } + + private: + template + void add_over_internal(T&& element_or_set) { + if (this->is_value()) { + this->get_value()->add_over(std::forward(element_or_set)); + } else if (this->is_bottom()) { + this->set_to_value(Value( + /* over */ Set(std::forward(element_or_set)), + /* under */ {})); + } + } + + template + void add_under_internal(T&& element_or_set) { + if (this->is_value()) { + this->get_value()->add_under(std::forward(element_or_set)); + } else if (this->is_bottom()) { + this->set_to_value(Value(std::forward(element_or_set))); + } + } +}; + +namespace over_under_set_impl { + +template +class OverUnderSetValue final : public AbstractValue> { + public: + using Element = typename Set::value_type; + + OverUnderSetValue() = default; + + explicit OverUnderSetValue(Element e) + : OverUnderSetValue(Set{std::move(e)}) {} + + explicit OverUnderSetValue(std::initializer_list l) + : OverUnderSetValue(Set(l)) {} + + explicit OverUnderSetValue(Set over_and_under) + : m_over(over_and_under), m_under(std::move(over_and_under)) { + // Union is unnecessary. + } + + OverUnderSetValue(Set over, Set under) + : m_over(std::move(over)), m_under(std::move(under)) { + m_over.union_with(m_under); + } + + void clear() { + m_over.clear(); + m_under.clear(); + } + + AbstractValueKind kind() const { return AbstractValueKind::Value; } + + bool empty() const { return m_over.empty(); } + + const Set& over() const { return m_over; } + + const Set& under() const { return m_under; } + + void add_over(const Element& e) { m_over.insert(e); } + + void add_over(const Set& set) { m_over.union_with(set); } + + void add_under(const Element& e) { + m_over.insert(e); + m_under.insert(e); + } + + void add_under(const Set& set) { + m_over.union_with(set); + m_under.union_with(set); + } + + void add(const OverUnderSetValue& other) { + m_over.union_with(other.m_over); + m_under.union_with(other.m_under); + } + + bool leq(const OverUnderSetValue& other) const { + return m_over.is_subset_of(other.m_over) && + other.m_under.is_subset_of(m_under); + } + + bool equals(const OverUnderSetValue& other) const { + return m_over.equals(other.m_over) && m_under.equals(other.m_under); + } + + AbstractValueKind join_with(const OverUnderSetValue& other) { + m_over.union_with(other.m_over); + m_under.intersection_with(other.m_under); + return AbstractValueKind::Value; + } + + AbstractValueKind widen_with(const OverUnderSetValue& other) { + return join_with(other); + } + + AbstractValueKind meet_with(const OverUnderSetValue& other) { + m_over.intersection_with(other.m_over); + m_under.union_with(other.m_under); + return m_under.is_subset_of(m_over) ? AbstractValueKind::Value + : AbstractValueKind::Bottom; + } + + AbstractValueKind narrow_with(const OverUnderSetValue& other) { + return meet_with(other); + } + + friend std::ostream& operator<<(std::ostream& out, + const OverUnderSetValue& value) { + if (value.empty()) { + out << "{}"; + } else { + out << "{over=" << value.m_over << ", under=" << value.m_under << "}"; + } + return out; + } + + private: + // Invariant: m_under.is_subset_of(m_over) + Set m_over; + Set m_under; +}; + +} // namespace over_under_set_impl +} // namespace sparta diff --git a/sparta/include/sparta/PatriciaTreeOverUnderSetAbstractDomain.h b/sparta/include/sparta/PatriciaTreeOverUnderSetAbstractDomain.h index b051e1758de..25dcaf8f893 100644 --- a/sparta/include/sparta/PatriciaTreeOverUnderSetAbstractDomain.h +++ b/sparta/include/sparta/PatriciaTreeOverUnderSetAbstractDomain.h @@ -7,119 +7,11 @@ #pragma once -#include - -#include -#include +#include #include namespace sparta { -namespace ptousad_impl { - -template -class OverUnderSetValue final - : public AbstractValue> { - public: - OverUnderSetValue() = default; - - explicit OverUnderSetValue(Element e) - : OverUnderSetValue(PatriciaTreeSet{std::move(e)}) {} - - explicit OverUnderSetValue(std::initializer_list l) - : OverUnderSetValue(PatriciaTreeSet(l)) {} - - explicit OverUnderSetValue(PatriciaTreeSet over_and_under) - : m_over(over_and_under), m_under(std::move(over_and_under)) { - // Union is unnecessary. - } - - OverUnderSetValue(PatriciaTreeSet over, - PatriciaTreeSet under) - : m_over(std::move(over)), m_under(std::move(under)) { - m_over.union_with(m_under); - } - - void clear() { - m_over.clear(); - m_under.clear(); - } - - AbstractValueKind kind() const { return AbstractValueKind::Value; } - - bool empty() const { return m_over.empty(); } - - const PatriciaTreeSet& over() const { return m_over; } - - const PatriciaTreeSet& under() const { return m_under; } - - void add_over(Element e) { m_over.insert(std::move(e)); } - - void add_over(const PatriciaTreeSet& set) { m_over.union_with(set); } - - void add_under(Element e) { - m_over.insert(e); - m_under.insert(std::move(e)); - } - - void add_under(const PatriciaTreeSet& set) { - m_over.union_with(set); - m_under.union_with(set); - } - - void add(const OverUnderSetValue& other) { - m_over.union_with(other.m_over); - m_under.union_with(other.m_under); - } - - bool leq(const OverUnderSetValue& other) const { - return m_over.is_subset_of(other.m_over) && - other.m_under.is_subset_of(m_under); - } - - bool equals(const OverUnderSetValue& other) const { - return m_over.equals(other.m_over) && m_under.equals(other.m_under); - } - - AbstractValueKind join_with(const OverUnderSetValue& other) { - m_over.union_with(other.m_over); - m_under.intersection_with(other.m_under); - return AbstractValueKind::Value; - } - - AbstractValueKind widen_with(const OverUnderSetValue& other) { - return join_with(other); - } - - AbstractValueKind meet_with(const OverUnderSetValue& other) { - m_over.intersection_with(other.m_over); - m_under.union_with(other.m_under); - return m_under.is_subset_of(m_over) ? AbstractValueKind::Value - : AbstractValueKind::Bottom; - } - - AbstractValueKind narrow_with(const OverUnderSetValue& other) { - return meet_with(other); - } - - friend std::ostream& operator<<(std::ostream& out, - const OverUnderSetValue& value) { - if (value.empty()) { - out << "{}"; - } else { - out << "{over=" << value.m_over << ", under=" << value.m_under << "}"; - } - return out; - } - - private: - // Invariant: m_under.is_subset_of(m_over) - PatriciaTreeSet m_over; - PatriciaTreeSet m_under; -}; - -} // namespace ptousad_impl - /** * An implementation of powerset abstract domains that computes both an over- * and under-approximation using Patricia trees. @@ -128,136 +20,7 @@ class OverUnderSetValue final * pointers to objects. */ template -class PatriciaTreeOverUnderSetAbstractDomain final - : public AbstractDomainScaffolding< - ptousad_impl::OverUnderSetValue, - PatriciaTreeOverUnderSetAbstractDomain> { - public: - using Value = ptousad_impl::OverUnderSetValue; - - /* Return the empty over-under set. */ - PatriciaTreeOverUnderSetAbstractDomain() { this->set_to_value(Value()); } - - explicit PatriciaTreeOverUnderSetAbstractDomain(AbstractValueKind kind) - : AbstractDomainScaffolding< - ptousad_impl::OverUnderSetValue, - PatriciaTreeOverUnderSetAbstractDomain>(kind) {} - - explicit PatriciaTreeOverUnderSetAbstractDomain(Element e) { - this->set_to_value(Value(std::move(e))); - } - - explicit PatriciaTreeOverUnderSetAbstractDomain( - std::initializer_list l) { - this->set_to_value(Value(l)); - } - - explicit PatriciaTreeOverUnderSetAbstractDomain( - PatriciaTreeSet set) { - this->set_to_value(Value(std::move(set))); - } - - explicit PatriciaTreeOverUnderSetAbstractDomain( - PatriciaTreeSet over, PatriciaTreeSet under) { - this->set_to_value(Value(std::move(over), std::move(under))); - } - - static PatriciaTreeOverUnderSetAbstractDomain bottom() { - return PatriciaTreeOverUnderSetAbstractDomain(AbstractValueKind::Bottom); - } - - static PatriciaTreeOverUnderSetAbstractDomain top() { - return PatriciaTreeOverUnderSetAbstractDomain(AbstractValueKind::Top); - } - - bool empty() const { return this->is_value() && this->get_value()->empty(); } - - const PatriciaTreeSet& over() const { - RUNTIME_CHECK(this->kind() == AbstractValueKind::Value, - invalid_abstract_value() - << expected_kind(AbstractValueKind::Value) - << actual_kind(this->kind())); - return this->get_value()->over(); - } - - const PatriciaTreeSet& under() const { - RUNTIME_CHECK(this->kind() == AbstractValueKind::Value, - invalid_abstract_value() - << expected_kind(AbstractValueKind::Value) - << actual_kind(this->kind())); - return this->get_value()->under(); - } - - void add_over(Element e) { add_over_internal(std::move(e)); } - - void add_over(const PatriciaTreeSet& set) { add_over_internal(set); } - - void add_over(PatriciaTreeSet&& set) { - add_over_internal(std::move(set)); - } - - void add_under(Element e) { add_under_internal(std::move(e)); } - - void add_under(const PatriciaTreeSet& set) { - add_under_internal(set); - } - - void add_under(PatriciaTreeSet&& set) { - add_under_internal(std::move(set)); - } - - void add(const PatriciaTreeOverUnderSetAbstractDomain& other) { - if (this->is_top() || other.is_bottom()) { - return; - } else if (other.is_top()) { - this->set_to_top(); - } else if (this->is_bottom()) { - this->set_to_value(*other.get_value()); - } else { - this->get_value()->add(*other.get_value()); - } - } - - friend std::ostream& operator<<( - std::ostream& out, const PatriciaTreeOverUnderSetAbstractDomain& x) { - using namespace sparta; - switch (x.kind()) { - case AbstractValueKind::Bottom: { - out << "_|_"; - break; - } - case AbstractValueKind::Top: { - out << "T"; - break; - } - case AbstractValueKind::Value: { - out << *x.get_value(); - break; - } - } - return out; - } - - private: - template - void add_over_internal(T&& element_or_set) { - if (this->is_value()) { - this->get_value()->add_over(std::forward(element_or_set)); - } else if (this->is_bottom()) { - this->set_to_value(Value( - /* over */ PatriciaTreeSet(std::forward(element_or_set)), - /* under */ {})); - } - } - - template - void add_under_internal(T&& element_or_set) { - if (this->is_value()) { - this->get_value()->add_under(std::forward(element_or_set)); - } else if (this->is_bottom()) { - this->set_to_value(Value(std::forward(element_or_set))); - } - } -}; +using PatriciaTreeOverUnderSetAbstractDomain = + OverUnderSetAbstractDomain>; } // namespace sparta