include/ConstantAbstractDomain.h (102 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 <sstream>
#include <type_traits>
#include <utility>
#include <boost/optional.hpp>
#include "AbstractDomain.h"
namespace sparta {
/*
* This abstract domain combinator constructs the lattice of constants of a
* certain type (also called the flat lattice or the three-level lattice). For
* more detail on constant propagation please see:
*
* https://www.cs.utexas.edu/users/lin/cs380c/wegman.pdf
*
* For example, the lattice of integer constants:
*
* TOP
* / | \
* ... -2 -1 0 1 2 ....
* \ | /
* _|_
*
* can be implemented as follows:
*
* using Int32ConstantDomain = ConstantAbstractDomain<int32_t>;
*
* Note: The base constant elements should be comparable using `operator==()`.
*/
namespace acd_impl {
template <typename Constant>
class ConstantAbstractValue final
: public AbstractValue<ConstantAbstractValue<Constant>> {
public:
~ConstantAbstractValue() {
static_assert(std::is_default_constructible<Constant>::value,
"Constant is not default constructible");
static_assert(std::is_copy_constructible<Constant>::value,
"Constant is not copy constructible");
static_assert(std::is_copy_assignable<Constant>::value,
"Constant is not copy assignable");
}
ConstantAbstractValue() = default;
explicit ConstantAbstractValue(const Constant& constant)
: m_constant(constant) {}
void clear() override {}
AbstractValueKind kind() const override { return AbstractValueKind::Value; }
bool leq(const ConstantAbstractValue& other) const override {
return equals(other);
}
bool equals(const ConstantAbstractValue& other) const override {
return m_constant == other.get_constant();
}
AbstractValueKind join_with(const ConstantAbstractValue& other) override {
if (equals(other)) {
return AbstractValueKind::Value;
}
return AbstractValueKind::Top;
}
AbstractValueKind widen_with(const ConstantAbstractValue& other) override {
return join_with(other);
}
AbstractValueKind meet_with(const ConstantAbstractValue& other) override {
if (equals(other)) {
return AbstractValueKind::Value;
}
return AbstractValueKind::Bottom;
}
AbstractValueKind narrow_with(const ConstantAbstractValue& other) override {
return meet_with(other);
}
const Constant& get_constant() const { return m_constant; }
private:
Constant m_constant;
};
} // namespace acd_impl
template <typename Constant>
class ConstantAbstractDomain final
: public AbstractDomainScaffolding<
acd_impl::ConstantAbstractValue<Constant>,
ConstantAbstractDomain<Constant>> {
public:
using ConstantType = Constant;
ConstantAbstractDomain() { this->set_to_top(); }
explicit ConstantAbstractDomain(const Constant& cst) {
this->set_to_value(acd_impl::ConstantAbstractValue<Constant>(cst));
}
explicit ConstantAbstractDomain(AbstractValueKind kind)
: AbstractDomainScaffolding<acd_impl::ConstantAbstractValue<Constant>,
ConstantAbstractDomain<Constant>>(kind) {}
boost::optional<Constant> get_constant() const {
return (this->kind() == AbstractValueKind::Value)
? boost::optional<Constant>(this->get_value()->get_constant())
: boost::none;
}
static ConstantAbstractDomain bottom() {
return ConstantAbstractDomain(AbstractValueKind::Bottom);
}
static ConstantAbstractDomain top() {
return ConstantAbstractDomain(AbstractValueKind::Top);
}
friend std::ostream& operator<<(
std::ostream& out,
const typename sparta::ConstantAbstractDomain<Constant>& x) {
using namespace sparta;
switch (x.kind()) {
case AbstractValueKind::Bottom: {
out << "_|_";
break;
}
case AbstractValueKind::Top: {
out << "T";
break;
}
case AbstractValueKind::Value: {
out << *x.get_constant();
break;
}
}
return out;
}
};
} // namespace sparta