#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/YAMLTraits.h" #include "revng/ADT/KeyTraits.h" #include "revng/ADT/KeyedObjectContainer.h" #include "revng/ADT/KeyedObjectTraits.h" #include "revng/Support/Assert.h" // // has_yaml // namespace tupletree::detail { using namespace llvm::yaml; template constexpr bool has_yaml_v = has_DocumentListTraits::value or has_MappingTraits::value or has_SequenceTraits::value or has_BlockScalarTraits::value or has_CustomMappingTraits::value or has_PolymorphicTraits::value or has_ScalarTraits::value or has_ScalarEnumerationTraits::value; struct NoYaml {}; } // namespace tupletree::detail template constexpr bool has_yaml_v = tupletree::detail::has_yaml_v; static_assert(!has_yaml_v); static_assert(has_yaml_v); static_assert(has_yaml_v>); template using enable_if_has_yaml_t = std::enable_if_t, K>; template using enable_if_has_not_yaml_t = std::enable_if_t, K>; // // 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; } // // TupleLikeTraits // template struct TupleLikeTraits { // static const char *name(); // template // static const char *fieldName(); }; // // Implementation of MappingTraits for TupleLikeTraits implementors // template struct TupleLikeMappingTraits { // Recursive step template static void mapping(llvm::yaml::IO &io, T &Obj) { // Define the field using getTupleFieldName and the associated field io.mapRequired(TupleLikeTraits::template fieldName(), get(Obj)); // Recur mapping(io, Obj); } // Base case template<> void mapping>(llvm::yaml::IO &io, T &Obj) {} }; // // visit implementation // namespace tupletree::detail { template enable_if_tuple_end_t visitTuple(Visitor &V, T &Obj) { } template enable_if_not_tuple_end_t visitTuple(Visitor &V, T &Obj) { // Visit the field visit(V, get(Obj)); // Visit next element in tuple visitTuple(V, Obj); } } // namespace tupletree::detail // Tuple-like template enable_if_has_tuple_size_t visit(Visitor &V, T &Obj) { V.preVisit(Obj); tupletree::detail::visitTuple(V, Obj); V.postVisit(Obj); } // Container-like template enable_if_is_container_t visit(Visitor &V, T &Obj) { V.preVisit(Obj); using value_type = typename T::value_type; for (value_type &Element : Obj) { visit(V, Element); } V.postVisit(Obj); } // All the others template std::enable_if_t or has_tuple_size_v)> visit(Visitor &V, T &Element) { V.preVisit(Element); V.postVisit(Element); } /// Default visitor, doing nothing struct DefaultTupleTreeVisitor { template void preVisit(T &) {} template void postVisit(T &) {} }; // // tupleIndexByName // template enable_if_tuple_end_t tupleIndexByName(llvm::StringRef Name) { return -1; } template enable_if_not_tuple_end_t tupleIndexByName(llvm::StringRef Name) { llvm::StringRef ThisName = TupleLikeTraits::template fieldName(); if (Name == ThisName) return I; else return tupleIndexByName(Name); } // // getByKey // namespace tupletree::detail { template enable_if_tuple_end_t getByKeyTuple(RootT &M, KeyT Key) { return nullptr; } template enable_if_not_tuple_end_t getByKeyTuple(RootT &M, KeyT Key) { if (I == Key) { using tuple_element = typename std::tuple_element::type; revng_assert((std::is_same_v) ); return reinterpret_cast(&get(M)); } else { return getByKeyTuple(M, Key); } } } // namespace tupletree::detail template enable_if_has_tuple_size_t getByKey(RootT &M, KeyT Key) { return tupletree::detail::getByKeyTuple(M, Key); } template enable_if_is_container_t getByKey(RootT &M, KeyT Key) { for (auto &Element : M) { using KOT = KeyedObjectTraits>; if (KOT::key(Element) == Key) return ∈ } return nullptr; } // // callOnPathSteps (no instance) // template enable_if_has_tuple_size_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path); template enable_if_is_not_container_or_tuple_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { revng_abort(); } template enable_if_is_container_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); constexpr size_t IntsCount = KeyTraits::IntsCount; auto PathStep = slice<0, IntsCount>(Path); auto TargetKey = KeyTraits::fromInts(PathStep); V.template visitContainerElement(TargetKey); if (Path.size() > IntsCount) { callOnPathSteps(V, Path.slice(IntsCount)); } } namespace tupletree::detail { template enable_if_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { } template enable_if_not_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { if (Path[0] == I) { using next_type = typename std::tuple_element::type; V.template visitTupleElement(); if (Path.size() > 1) { callOnPathSteps(V, Path.slice(1)); } } else { callOnPathStepsTuple(V, Path); } } } // namespace tupletree::detail template enable_if_has_tuple_size_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { tupletree::detail::callOnPathStepsTuple(V, Path); } // // callOnPathSteps (with instance) // namespace tupletree::detail { template enable_if_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { } template enable_if_not_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { if (Path[0] == I) { using next_type = typename std::tuple_element::type; next_type &Element = get(M); V.template visitTupleElement(Element); if (Path.size() > 1) { callOnPathSteps(V, Path.slice(1), Element); } } else { callOnPathStepsTuple(V, Path, M); } } } // namespace tupletree::detail template enable_if_has_tuple_size_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { tupletree::detail::callOnPathStepsTuple(V, Path, M); } template enable_if_is_container_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); constexpr size_t IntsCount = KeyTraits::IntsCount; auto PathStep = slice<0, IntsCount>(Path); auto TargetKey = KeyTraits::fromInts(PathStep); value_type *Matching = nullptr; for (value_type &Element : M) { using KOT = KeyedObjectTraits; if (KOT::key(Element) == TargetKey) { Matching = ∈ break; } } revng_check(Matching != nullptr); V.template visitContainerElement(TargetKey, *Matching); if (Path.size() > IntsCount) { callOnPathSteps(V, Path.slice(IntsCount), *Matching); } } template enable_if_is_not_container_or_tuple_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { revng_abort(); } // // callByPath (no instance) // namespace tupletree::detail { template struct CallByPathVisitor { size_t PathSize; Visitor &V; template void visitTupleElement() { --PathSize; if (PathSize == 0) V.template visitTupleElement(); } template void visitContainerElement(KeyT Key) { constexpr size_t IntsCount = KeyTraits::IntsCount; PathSize -= IntsCount; if (PathSize == 0) V.template visitContainerElement(Key); } }; } // namespace tupletree::detail template void callByPath(Visitor &V, const KeyIntVector &Path) { using namespace tupletree::detail; CallByPathVisitor CBPV{ Path.size(), V }; callOnPathSteps(CBPV, Path); } // // callByPath (with instance) // namespace tupletree::detail { template struct CallByPathVisitorWithInstance { size_t PathSize; Visitor &V; template void visitTupleElement(K &Element) { --PathSize; if (PathSize == 0) V.template visitTupleElement(Element); } template void visitContainerElement(KeyT Key, K &Element) { constexpr size_t IntsCount = KeyTraits::IntsCount; PathSize -= IntsCount; if (PathSize == 0) V.template visitContainerElement(Key, Element); } }; } // namespace tupletree::detail template void callByPath(Visitor &V, const KeyIntVector &Path, RootT &M) { using namespace tupletree::detail; CallByPathVisitorWithInstance CBPV{ Path.size(), V }; callOnPathSteps(CBPV, Path, M); } // // getByPath // namespace tupletree::detail { template struct GetByPathVisitor { ResultT *Result = nullptr; template void visitContainerElement(KeyT, K &) { Result = nullptr; } template void visitContainerElement(KeyT, ResultT &Element) { Result = ∈ } template void visitTupleElement(K &) { Result = nullptr; } template void visitTupleElement(ResultT &Element) { Result = ∈ } }; } // namespace tupletree::detail template ResultT *getByPath(const KeyIntVector &Path, RootT &M) { using namespace tupletree::detail; GetByPathVisitor GBPV; callByPath(GBPV, Path, M); return GBPV.Result; } // // pathAsString // namespace tupletree::detail { class DumpPathVisitor { private: llvm::raw_string_ostream Stream; public: DumpPathVisitor(std::string &Result) : Stream(Result) {} template void visitTupleElement() { Stream << "/" << TupleLikeTraits::template fieldName(); } template void visitContainerElement(KeyT Key) { Stream << "/" << KeyTraits::toString(Key); } }; } // namespace tupletree::detail template std::string pathAsString(const KeyIntVector &Path) { std::string Result; { tupletree::detail::DumpPathVisitor PV(Result); callOnPathSteps(PV, Path); } return Result; } // // FOR_EACH macro implemenation // #define GET_MACRO(_0, \ _1, \ _2, \ _3, \ _4, \ _5, \ _6, \ _7, \ _8, \ _9, \ _10, \ _11, \ _12, \ _13, \ _14, \ _15, \ _16, \ NAME, \ ...) \ NAME #define NUMARGS(...) \ GET_MACRO(_0, \ __VA_ARGS__, \ 16, \ 15, \ 14, \ 13, \ 12, \ 11, \ 10, \ 9, \ 8, \ 7, \ 6, \ 5, \ 4, \ 3, \ 2, \ 1) #define FE_0(ACTION, TOTAL, ARG) #define FE_1(ACTION, TOTAL, ARG, X) ACTION(ARG, (TOTAL) -0, X) #define FE_2(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -1, X) \ FE_1(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_3(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -2, X) \ FE_2(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_4(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -3, X) \ FE_3(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_5(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -4, X) \ FE_4(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_6(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -5, X) \ FE_5(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_7(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -6, X) \ FE_6(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_8(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -7, X) \ FE_7(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_9(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -8, X) \ FE_8(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_10(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -9, X) \ FE_9(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_11(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -10, X) \ FE_10(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_12(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -11, X) \ FE_11(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_13(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -12, X) \ FE_12(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_14(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -13, X) \ FE_13(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_15(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -14, X) \ FE_14(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_16(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -15, X) \ FE_15(ACTION, TOTAL, ARG, __VA_ARGS__) /// Calls ACTION(ARG, INDEX, VA_ARG) for each VA_ARG in ... #define FOR_EACH(ACTION, ARG, ...) \ GET_MACRO(_0, \ __VA_ARGS__, \ FE_16, \ FE_15, \ FE_14, \ FE_13, \ FE_12, \ FE_11, \ FE_10, \ FE_9, \ FE_8, \ FE_7, \ FE_6, \ FE_5, \ FE_4, \ FE_3, \ FE_2, \ FE_1, \ FE_0) \ (ACTION, (NUMARGS(__VA_ARGS__) - 1), ARG, __VA_ARGS__) // // Macros to transform struct in tuple-like // #define TUPLE_ELEMENTS(class, index, field) \ template<> \ struct std::tuple_element { \ using type = decltype(class ::field); \ }; #define GET_IMPLEMENTATIONS(class, index, field) \ else if constexpr (I == index) return x.field; #define GET_TUPLE_FIELD_NAME(class, index, field) \ template<> \ const char *fieldName() { \ return #field; \ } #define INTROSPECTION_1(class, ...) \ template<> \ struct std::tuple_size \ : std::integral_constant {}; \ \ FOR_EACH(TUPLE_ELEMENTS, class, __VA_ARGS__) \ \ template<> \ struct TupleLikeTraits { \ static const char *name() { return #class; } \ \ template \ static const char *fieldName(); \ \ FOR_EACH(GET_TUPLE_FIELD_NAME, class, __VA_ARGS__) \ }; #define INTROSPECTION_2(class, ...) \ template \ auto &get(class &&x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } \ \ template \ const auto &get(const class &x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } \ \ template \ auto &get(class &x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } #define INTROSPECTION(class, ...) \ INTROSPECTION_1(class, __VA_ARGS__) \ INTROSPECTION_2(class, __VA_ARGS__) #define INTROSPECTION_NS(ns, class, ...) \ INTROSPECTION_1(ns::class, __VA_ARGS__) \ namespace ns { \ INTROSPECTION_2(class, __VA_ARGS__) \ }