#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" #include "revng/ADT/Concepts.h" #include "revng/Support/Debug.h" // // always_true and always_false // // Since an assert in the `else` branch of an `if_constexpr` condition said // branch gets instantiated if it doesn't depend on a template, these provide // an easy way to "fake" dependence on an arbitrary template parameter. // template struct type_always_false { constexpr static bool value = false; }; template constexpr inline bool type_always_false_v = type_always_false::value; template struct value_always_false { constexpr static bool value = false; }; template constexpr inline bool value_always_false_v = value_always_false::value; template struct type_always_true { constexpr static bool value = false; }; template constexpr inline bool type_always_true_v = type_always_true::value; template struct value_always_true { constexpr static bool value = false; }; template constexpr inline bool value_always_true_v = value_always_true::value; //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// namespace revng { namespace detail { template using ReturnType = decltype(std::declval()(*std::declval())); template> class ProxyMappedIteratorImpl : public llvm::mapped_iterator { struct IteratorProxy { IteratorProxy(FuncReturnTy &&Value) : Temporary(std::move(Value)) {} FuncReturnTy *const operator->() { return &Temporary; } FuncReturnTy const *const operator->() const { return &Temporary; } private: FuncReturnTy Temporary; }; public: using llvm::mapped_iterator::mapped_iterator; using reference = std::decay_t; IteratorProxy operator->() { return llvm::mapped_iterator::operator*(); } IteratorProxy const operator->() const { return llvm::mapped_iterator::operator*(); } }; template using ItImpl = std::conditional_t>, ProxyMappedIteratorImpl, llvm::mapped_iterator>; } // namespace detail /// `revng::mapped_iterator` is a specialized version of /// `llvm::mapped_iterator`. /// /// It can act as an in-place replacement since it doesn't change the behavior /// in most cases. The main difference is the fact that when the iterator uses /// a temporary as a way of remembering its position its lifetime is /// explicitly prolonged to prevent it from being deleted prematurely (like /// inside the `operator->` call). template using mapped_iterator = revng::detail::ItImpl; // `map_iterator` - Provide a convenient way to create `mapped_iterator`s, // just like `make_pair` is useful for creating pairs... template inline auto map_iterator(ItTy I, FuncTy F) { return mapped_iterator(std::move(I), std::move(F)); }; template auto map_range(ContainerTy &&C, FuncTy F) { return llvm::make_range(map_iterator(C.begin(), F), map_iterator(C.end(), F)); } auto dereferenceIterator(auto Iter) { return llvm::map_iterator(Iter, [](const auto &Ptr) -> decltype(*Ptr) & { return *Ptr; }); } namespace detail { template using DIT = decltype(dereferenceIterator(std::declval())); } template using DereferenceIteratorType = revng::detail::DIT; template using DereferenceRangeType = llvm::iterator_range>; auto dereferenceRange(auto &&Range) { return llvm::make_range(dereferenceIterator(Range.begin()), dereferenceIterator(Range.end())); } template auto mapToValueIterator(Iterator It) { const auto GetSecond = [](auto &Pair) -> auto & { return Pair.second; }; return llvm::map_iterator(It, GetSecond); } template using MapToValueIteratorType = decltype(mapToValueIterator(std::declval())); } // namespace revng // // skip // namespace revng::detail { // Remove these incomplete iterator testers after we update to libc++-13+ // with standard library concept support. // NOTE: they are VERY basic, don't rely on them too much. template using Category = typename std::iterator_traits::iterator_category; template concept InputOnly = std::is_same_v, std::input_iterator_tag>; template concept OutputOnly = std::is_same_v, std::output_iterator_tag>; template concept ForwardOnly = std::is_same_v, std::forward_iterator_tag>; template concept BidirectionalOnly = std::is_same_v, std::bidirectional_iterator_tag>; template concept RandomAccessOnly = std::is_same_v, std::random_access_iterator_tag>; template concept ContiguousOnly = std::is_same_v, std::contiguous_iterator_tag>; // clang-format off template concept contiguous_iterator = ContiguousOnly; template concept random_access_iterator = contiguous_iterator || RandomAccessOnly; template concept bidirectional_iterator = random_access_iterator || BidirectionalOnly; template concept forward_iterator = bidirectional_iterator || ForwardOnly; template concept input_iterator = forward_iterator || InputOnly; template concept input_or_output_iterator = input_iterator || OutputOnly; // clang-format on template inline auto skipImpl(IteratorType &&From, IteratorType &&To, std::size_t Front = 0, std::size_t Back = 0) -> llvm::iterator_range { std::ptrdiff_t TotalSkippedCount = Front + Back; if constexpr (forward_iterator) { // We cannot compute the assert on the input iterators because it's // going to consume them. revng_assert(std::distance(From, To) >= TotalSkippedCount); } std::decay_t Begin{ From }; std::advance(Begin, Front); std::decay_t End{ To }; std::advance(End, -(std::ptrdiff_t) Back); return llvm::make_range(std::move(Begin), std::move(End)); } template inline decltype(auto) skip(T &&From, T &&To, std::size_t Front = 0, std::size_t Back = 0) { return skipImpl(std::forward(From), std::forward(To), Front, Back); } template inline decltype(auto) // NOLINTNEXTLINE skip_front(T &&From, T &&To, std::size_t SkippedCount = 1) { return skipImpl(std::forward(From), std::forward(To), SkippedCount, 0); } template inline decltype(auto) // NOLINTNEXTLINE skip_back(T &&From, T &&To, std::size_t SkippedCount = 1) { return skipImpl(std::forward(From), std::forward(To), 0, SkippedCount); } } // namespace revng::detail template inline decltype(auto) skip(T &&Range, std::size_t Front = 0, std::size_t Back = 0) { return revng::detail::skip(Range.begin(), Range.end(), Front, Back); } template // NOLINTNEXTLINE inline decltype(auto) skip_front(T &&Range, std::size_t SkippedCount = 1) { return revng::detail::skip_front(Range.begin(), Range.end(), SkippedCount); } template // NOLINTNEXTLINE inline decltype(auto) skip_back(T &&Range, std::size_t SkippedCount = 1) { return revng::detail::skip_back(Range.begin(), Range.end(), SkippedCount); } // // slice // /// Copy into a std::array a slice of an llvm::ArrayRef template std::array slice(llvm::ArrayRef Old) { std::array Result; auto StartIt = Old.begin() + Start; std::copy(StartIt, StartIt + Size, Result.begin()); return Result; } /// Copy into a std::array a slice of a std::array template std::array slice(const std::array &Old) { std::array Result; auto StartIt = Old.begin() + Start; std::copy(StartIt, StartIt + Size, Result.begin()); return Result; } /// Simple helper function asserting a pointer is not a `nullptr` template inline T *notNull(T *Pointer) { revng_assert(Pointer != nullptr); return Pointer; } inline llvm::ArrayRef toArrayRef(llvm::StringRef Data) { auto Pointer = reinterpret_cast(Data.data()); return llvm::ArrayRef(Pointer, Data.size()); } // // append // template auto append(FromType &&From, ToType &To) { size_t ExistingElementCount = To.size(); To.resize(ExistingElementCount + From.size()); return llvm::copy(From, std::next(To.begin(), ExistingElementCount)); } /// Intersects two std::sets template std::set intersect(const std::set &First, const std::set &Last) { std::set Output; std::set_intersection(First.begin(), First.end(), Last.begin(), Last.end(), std::inserter(Output, Output.begin())); return Output; } // // constexpr repeat // namespace detail { template constexpr void constexprRepeatImpl(std::index_sequence, TemplatedCallableType &&Callable) { (Callable.template operator()(), ...); } template constexpr bool constexprAndImpl(std::index_sequence, TemplatedCallableType &&Callable) { return (Callable.template operator()() && ...); } template constexpr bool constexprOrImpl(std::index_sequence, TemplatedCallableType &&Callable) { return (Callable.template operator()() || ...); } } // namespace detail template constexpr void constexprRepeat(CallableType &&Callable) { detail::constexprRepeatImpl(std::make_index_sequence(), std::forward(Callable)); } template constexpr bool constexprAnd(CallableType &&Callable) { return detail::constexprAndImpl(std::make_index_sequence(), std::forward(Callable)); } template constexpr bool constexprOr(CallableType &&Callable) { return detail::constexprOrImpl(std::make_index_sequence(), std::forward(Callable)); } namespace examples { using namespace std::string_view_literals; template consteval std::size_t fullSize(std::array Components, std::string_view Separator) { std::size_t Result = Separator.size() * Count; constexprRepeat([&Result, &Components] { Result += std::get(Components).size(); }); return Result; } inline constexpr std::array Components = { "instruction"sv, "0x401000:Code_x86_64"sv, "0x402000:Code_x86_64"sv, "0x403000:Code_x86_64"sv }; static_assert(fullSize(Components, "/"sv) == 75); } // namespace examples // // constexpr split // namespace detail { template inline constexpr bool constexprSplitHelper(std::array &Result, std::string_view Separator, std::string_view Input) { std::size_t Position = Input.find(Separator); if constexpr (I < N - 1) { if (Position == std::string_view::npos) return false; Result[I] = Input.substr(0, Position); return constexprSplitHelper(Result, Separator, Input.substr(Position + 1)); } else { if (Position != std::string_view::npos) return false; Result[I] = Input; return true; } } } // namespace detail /// I'm forced to implement my own split because `llvm::StringRef`'s alternative /// is not `constexpr`-compatible. /// /// This also uses `std::string_view` instead of `llvm::StringRef` because its /// `find` member is constexpr - hence at least that member doesn't have to be /// reimplemented template inline constexpr std::optional> constexprSplit(std::string_view Separator, std::string_view Input) { if (std::array Result; detail::constexprSplitHelper(Result, Separator, Input)) return Result; else return std::nullopt; } inline void replaceAll(std::string &Input, const std::string &From, const std::string &To) { if (From.empty()) return; size_t Start = 0; while ((Start = Input.find(From, Start)) != std::string::npos) { Input.replace(Start, From.length(), To); Start += To.length(); } } // // `constexpr` versions of the llvm algorithm adaptors. // namespace revng { /// \note use `llvm::find` instead after it's made `constexpr`. template constexpr decltype(auto) find(R &&Range, const T &Value) { return std::find(std::begin(std::forward(Range)), std::end(std::forward(Range)), Value); } /// \note use `llvm::find_if` instead after it's made `constexpr`. template // NOLINTNEXTLINE constexpr decltype(auto) find_if(R &&Range, CallableType &&Callable) { return std::find_if(std::begin(std::forward(Range)), std::end(std::forward(Range)), std::forward(Callable)); } /// \note use `llvm::find_if_not` instead after it's made `constexpr`. template // NOLINTNEXTLINE constexpr decltype(auto) find_if_not(R &&Range, CallableType &&Callable) { return std::find_if_not(std::begin(std::forward(Range)), std::end(std::forward(Range)), std::forward(Callable)); } /// \note `std::find_last` is introduced in c++23, /// replace with the llvm version when it's available. template // NOLINTNEXTLINE constexpr decltype(auto) find_last(R &&Range, const T &Value) { return std::find(std::rbegin(std::forward(Range)), std::rend(std::forward(Range)), Value); } /// \note `std::find_last_if` is introduced in c++23, /// replace with the llvm version when it's available. template // NOLINTNEXTLINE constexpr decltype(auto) find_last_if(R &&Range, CallableType &&Callable) { return std::find_if(std::rbegin(std::forward(Range)), std::rend(std::forward(Range)), std::forward(Callable)); } /// \note `std::find_last_if_not` is introduced in c++23, /// replace with the llvm version when it's available. template // NOLINTNEXTLINE constexpr decltype(auto) find_last_if_not(R &&Range, CallableType &&Callable) { return std::find_if_not(std::rbegin(std::forward(Range)), std::rend(std::forward(Range)), std::forward(Callable)); } /// \note use `llvm::is_contained` instead after it's made `constexpr`. template // NOLINTNEXTLINE constexpr bool is_contained(R &&Range, const T &Value) { return revng::find(std::forward(Range), Value) != std::end(Range); } template // NOLINTNEXTLINE constexpr bool is_contained_if(Range &&R, C &&L) { return find_if(std::forward(R), std::forward(L)) != std::end(R); } static_assert(is_contained(std::array{ 1, 2, 3 }, 2) == true); static_assert(is_contained(std::array{ 1, 2, 3 }, 4) == false); } // namespace revng // // Some views from the STL. // TODO: remove these after updating the libc++ version. // template // NOLINTNEXTLINE auto as_rvalue(RangeType &&Range) { return llvm::make_range(std::make_move_iterator(Range.begin()), std::make_move_iterator(Range.end())); }