diff --git a/libcxx/include/__assert b/libcxx/include/__assert index 90eaa6023587b..5a5cad472425c 100644 --- a/libcxx/include/__assert +++ b/libcxx/include/__assert @@ -27,9 +27,7 @@ // optimization intent. See https://discourse.llvm.org/t/llvm-assume-blocks-optimization/71609 for a // discussion. #if __has_builtin(__builtin_assume) -# define _LIBCPP_ASSUME(expression) \ - (_LIBCPP_DIAGNOSTIC_PUSH _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wassume") \ - __builtin_assume(static_cast(expression)) _LIBCPP_DIAGNOSTIC_POP) +# define _LIBCPP_ASSUME(expression) __builtin_assume(static_cast(expression)) #else # define _LIBCPP_ASSUME(expression) ((void)0) #endif diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h index d12750d1f81ac..27793fca6490c 100644 --- a/libcxx/include/__iterator/bounded_iter.h +++ b/libcxx/include/__iterator/bounded_iter.h @@ -105,6 +105,18 @@ struct __bounded_iter { __begin <= __current, "__bounded_iter(current, begin, end): current and begin are inconsistent"); _LIBCPP_ASSERT_INTERNAL( __current <= __end, "__bounded_iter(current, begin, end): current and end are inconsistent"); + + // Add assumptions to help the compiler reason about bounds checks. For example, std::vector sets the end of + // the __bounded_iter's valid range to the capacity of the vector, not vector::end(). To translate container-end + // fenceposts into hardening-end fenceposts, we must know that container-end <= hardening-end. + pointer __begin_ptr = std::__to_address(__begin); + pointer __current_ptr = std::__to_address(__current); + pointer __end_ptr = std::__to_address(__end); + _LIBCPP_ASSUME(__begin_ptr <= __current_ptr); + _LIBCPP_ASSUME(__current_ptr <= __end_ptr); + (void)__begin_ptr; + (void)__current_ptr; + (void)__end_ptr; } template diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h index 4e0d76fbbe3de..64e9d8c25b84b 100644 --- a/libcxx/include/__vector/vector.h +++ b/libcxx/include/__vector/vector.h @@ -658,6 +658,11 @@ class vector { _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator __make_iter(pointer __p) _NOEXCEPT { #ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR + // `__bounded_iter` will tell the compiler that `__p` is bounded by `__begin_` and `__end_cap`, but nothing a priori + // relates `__p` to `__end_`. + _LIBCPP_ASSERT_INTERNAL(__p <= this->__end_, "vector::__make_iter passed an invalid pointer"); + _LIBCPP_ASSUME(__p <= this->__end_); + // Bound the iterator according to the capacity, rather than the size. // // Vector guarantees that iterators stay valid as long as no reallocation occurs even if new elements are inserted @@ -678,7 +683,12 @@ class vector { _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator __make_iter(const_pointer __p) const _NOEXCEPT { #ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR - // Bound the iterator according to the capacity, rather than the size. + // `__bounded_iter` will tell the compiler that `__p` is bounded by `__begin_` and `__end_cap`, but nothing a priori + // relates `__p` to `__end_`. + _LIBCPP_ASSERT_INTERNAL(__p <= this->__end_, "vector::__make_iter passed an invalid pointer"); + _LIBCPP_ASSUME(__p <= this->__end_); + + // Bound the iterator according to the capacity, rather than the size. See above. return std::__make_bounded_iter( std::__wrap_iter(__p), std::__wrap_iter(this->__begin_), diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h index dcd251d6997dd..b91b4ef6c9300 100644 --- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h +++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h @@ -448,6 +448,27 @@ void sequence_container_benchmarks(std::string container) { } }); } + + ///////////////////////// + // General usage patterns + ///////////////////////// + bench("iterate-whole-container", [cheap](auto& st) { + auto const size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, cheap); + DoNotOptimizeData(in); + + Container c(in.begin(), in.end()); + DoNotOptimizeData(c); + + auto use = [](auto& element) { benchmark::DoNotOptimize(element); }; + + for ([[maybe_unused]] auto _ : st) { + for (auto it = c.begin(); it != c.end(); ++it) { + use(*it); + } + } + }); } } // namespace support