Index: include/llvm/ADT/STLExtras.h =================================================================== --- include/llvm/ADT/STLExtras.h +++ include/llvm/ADT/STLExtras.h @@ -31,6 +31,7 @@ #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" namespace llvm { @@ -435,6 +436,146 @@ std::forward(t), std::forward(u), std::forward(args)...); } +/// Iterator wrapper that concatenates sequences together. +/// +/// This can concatenate different iterators, even with different types, into +/// a single iterator provided the value types of all the concatenated +/// iterators expose `reference` and `pointer` types that can be converted to +/// `ValueT &` and `ValueT *` respectively. It doesn't support more +/// interesting/customized pointer or reference types. +/// +/// Currently this only supports forward or higher iterator categories as +/// inputs and always exposes a forward iterator interface. +template +class concat_iterator + : public iterator_facade_base, + std::forward_iterator_tag, ValueT> { + typedef typename concat_iterator::iterator_facade_base BaseT; + + std::tuple iterators; + std::tuple end_iterators; + + // Attempts to increment a specific iterator. + // + // Returns true if it was able to increment the iterator. Returns false if + // the iterator is already at the end iterator. + template bool incrementHelper() { + auto &I = std::get(iterators); + auto &E = std::get(end_iterators); + if (I == E) + return false; + + ++I; + return true; + } + + /// Increments the first non-end iterator. + /// + /// It is an error to call this with all iterators at the end. + template void increment(index_sequence) { + // Build a sequence of functions to increment each iterator if possible. + auto IncrementHelperFns = {&concat_iterator::incrementHelper...}; + + // Loop over them, and stop as soon as we succeed at incrementing one. + for (auto &IncrementHelperFn : IncrementHelperFns) + if ((this->*IncrementHelperFn)()) + return; + + llvm_unreachable("Attempted to increment an end concat iterator!"); + } + + // Returns null if the specified iterator is at the end. Otherwise, + // dereferences the iterator and returns the address of the resulting + // reference. + template ValueT *getHelper() const { + auto &I = std::get(iterators); + auto &E = std::get(end_iterators); + if (I == E) + return nullptr; + + return &*I; + } + + // Finds the first non-end iterator, dereferences, and returns the resulting + // reference. + // + // It is an error to call this with all iterators at the end. + template ValueT &get(index_sequence) const { + // Build a sequence of functions to get from iterator if possible. + auto GetHelperFns = {&concat_iterator::getHelper...}; + + // Loop over them, and return the first result we find. + for (auto &GetHelperFn : GetHelperFns) + if (ValueT *P = (this->*GetHelperFn)()) + return *P; + + llvm_unreachable("Attempted to get a pointer from an end concat iterator!"); + } + +public: + /// Constructs an iterator from a squence of ranges. + /// + /// We need the full range to know how to switch between each of the + /// iterators. + template + explicit concat_iterator(RangeTs &&... Ranges) + : iterators(std::begin(Ranges)...), end_iterators(std::end(Ranges)...) {} + + using BaseT::operator++; + concat_iterator &operator++() { + increment(index_sequence_for()); + return *this; + } + + ValueT &operator*() const { return get(index_sequence_for()); } + + bool operator==(const concat_iterator &RHS) const { + return iterators == RHS.iterators; + } +}; + +namespace detail { +/// Helper to store a sequence of ranges being concatenated and access them. +/// +/// This is designed to facilitate providing actual storage when temporaries +/// are passed into the constructor such that we can use it as part of range +/// based for loops. +template class concat_range { +public: + typedef concat_iterator()))...> + iterator; + +private: + std::tuple Ranges; + + template iterator begin_impl(index_sequence) { + return iterator(std::get(Ranges)...); + } + template iterator end_impl(index_sequence) { + return iterator(make_range(std::end(std::get(Ranges)), + std::end(std::get(Ranges)))...); + } + +public: + iterator begin() { return begin_impl(index_sequence_for{}); } + iterator end() { return end_impl(index_sequence_for{}); } + concat_range(RangeTs &&... Ranges) + : Ranges(std::forward(Ranges)...) {} +}; +} + +/// Concatenated range across two or more ranges. +/// +/// The desired value type must be explicitly specified. +template +detail::concat_range concat(RangeTs &&... Ranges) { + static_assert(sizeof...(RangeTs) > 1, + "Need more than one range to concatenate!"); + return detail::concat_range( + std::forward(Ranges)...); +} + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// Index: include/llvm/IR/Module.h =================================================================== --- include/llvm/IR/Module.h +++ include/llvm/IR/Module.h @@ -590,69 +590,31 @@ /// @name Convenience iterators /// @{ - template class global_object_iterator_t { - friend Module; - - typename std::conditional::type - function_i, - function_e; - typename std::conditional::type global_i; - - typedef - typename std::conditional::type ModuleTy; - - global_object_iterator_t(ModuleTy &M) - : function_i(M.begin()), function_e(M.end()), - global_i(M.global_begin()) {} - global_object_iterator_t(ModuleTy &M, int) - : function_i(M.end()), function_e(M.end()), global_i(M.global_end()) {} - - public: - global_object_iterator_t &operator++() { - if (function_i != function_e) - ++function_i; - else - ++global_i; - return *this; - } - - typename std::conditional::type & - operator*() const { - if (function_i != function_e) - return *function_i; - else - return *global_i; - } - - bool operator!=(const global_object_iterator_t &other) const { - return function_i != other.function_i || global_i != other.global_i; - } - }; - - typedef global_object_iterator_t global_object_iterator; - typedef global_object_iterator_t + typedef concat_iterator + global_object_iterator; + typedef concat_iterator const_global_object_iterator; + iterator_range global_objects() { + return concat(functions(), globals()); + } + iterator_range global_objects() const { + return concat(functions(), globals()); + } + global_object_iterator global_object_begin() { - return global_object_iterator(*this); + return global_objects().begin(); } global_object_iterator global_object_end() { - return global_object_iterator(*this, 0); + return global_objects().end(); } const_global_object_iterator global_object_begin() const { - return const_global_object_iterator(*this); + return global_objects().begin(); } const_global_object_iterator global_object_end() const { - return const_global_object_iterator(*this, 0); - } - - iterator_range global_objects() { - return make_range(global_object_begin(), global_object_end()); - } - iterator_range global_objects() const { - return make_range(global_object_begin(), global_object_end()); + return global_objects().end(); } /// @} Index: unittests/ADT/STLExtrasTest.cpp =================================================================== --- unittests/ADT/STLExtrasTest.cpp +++ unittests/ADT/STLExtrasTest.cpp @@ -10,6 +10,7 @@ #include "llvm/ADT/STLExtras.h" #include "gtest/gtest.h" +#include #include using namespace llvm; @@ -253,4 +254,17 @@ EXPECT_EQ(1, count(v, 3)); EXPECT_EQ(1, count(v, 4)); } + +TEST(STLExtrasTest, ConcatRange) { + std::vector Expected = {1, 2, 3, 4, 5, 6, 7, 8}; + + std::vector V1234 = {1, 2, 3, 4}; + std::list L56 = {5, 6}; + SmallVector SV78 = {7, 8}; + + std::vector Test; + for (int &i : concat(V1234, L56, SV78)) + Test.push_back(i); + EXPECT_EQ(Expected, Test); +} }