#175913 removed that `__builtin_assume_dereferenceable(ptr, 0)` implies
`ptr != nullptr`, which should allow us to use the builtin with LLVM 23.
This reverts commit 776c09c212.
This commit is contained in:
parent
808a6ba345
commit
4304106dc2
10 changed files with 92 additions and 58 deletions
|
|
@ -607,6 +607,7 @@ set(files
|
|||
__memory/unique_temporary_buffer.h
|
||||
__memory/uses_allocator.h
|
||||
__memory/uses_allocator_construction.h
|
||||
__memory/valid_range.h
|
||||
__memory_resource/memory_resource.h
|
||||
__memory_resource/monotonic_buffer_resource.h
|
||||
__memory_resource/polymorphic_allocator.h
|
||||
|
|
@ -931,7 +932,6 @@ set(files
|
|||
__utility/in_place.h
|
||||
__utility/integer_sequence.h
|
||||
__utility/is_pointer_in_range.h
|
||||
__utility/is_valid_range.h
|
||||
__utility/lazy_synth_three_way_comparator.h
|
||||
__utility/move.h
|
||||
__utility/no_destroy.h
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#define _LIBCPP___ALGORITHM_FIND_IF_H
|
||||
|
||||
#include <__config>
|
||||
#include <__memory/valid_range.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
|
|
@ -21,6 +22,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
|||
template <class _InputIterator, class _Predicate>
|
||||
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
|
||||
find_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
|
||||
std::__assume_valid_range(__first, __last);
|
||||
|
||||
for (; __first != __last; ++__first)
|
||||
if (__pred(*__first))
|
||||
break;
|
||||
|
|
|
|||
74
libcxx/include/__memory/valid_range.h
Normal file
74
libcxx/include/__memory/valid_range.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___MEMORY_VALID_RANGE_H
|
||||
#define _LIBCPP___MEMORY_VALID_RANGE_H
|
||||
|
||||
#include <__algorithm/comp.h>
|
||||
#include <__assert>
|
||||
#include <__config>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__memory/assume_aligned.h>
|
||||
#include <__memory/pointer_traits.h>
|
||||
#include <__type_traits/is_constant_evaluated.h>
|
||||
#include <__type_traits/is_same.h>
|
||||
#include <__type_traits/remove_cvref.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
// A valid range as defined by the C++ Standard has the following constraints:
|
||||
// - [__first, __last) is dereferenceable
|
||||
// - __last is reachable from __first
|
||||
// - if __first and __last are contiguous iterators, the pointers they "decay to" are correctly aligned according to the
|
||||
// language rules for pointers
|
||||
|
||||
// This function attempts to detect invalid ranges as defined above. Specifically, it checks bullet (2). This also means
|
||||
// that it doesn't return whether a range is actually valid, but only whether a range is definitely not valid.
|
||||
// The checks may be extended in the future.
|
||||
template <class _Tp>
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") bool
|
||||
__is_valid_range(const _Tp* __first, const _Tp* __last) {
|
||||
if (__libcpp_is_constant_evaluated()) {
|
||||
// If this is not a constant during constant evaluation, that is because __first and __last are not
|
||||
// part of the same allocation. If they are part of the same allocation, we must still make sure they
|
||||
// are ordered properly.
|
||||
return __builtin_constant_p(__first <= __last) && __first <= __last;
|
||||
}
|
||||
|
||||
return !__less<>()(__last, __first);
|
||||
}
|
||||
|
||||
// This function allows the compiler to assume that [__first, __last) is a valid range as defined above.
|
||||
//
|
||||
// In practice, we only add explicit assumptions for bullets (1) and (3). These assumptions allow (currently only
|
||||
// clang-based compilers) to auto-vectorize algorithms that contain early returns.
|
||||
template <class _Iter, class _Sent>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
|
||||
__assume_valid_range([[__maybe_unused__]] _Iter&& __first, [[__maybe_unused__]] _Sent&& __last) {
|
||||
#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2300 && !defined(_LIBCPP_CXX03_LANG)
|
||||
if constexpr (__libcpp_is_contiguous_iterator<__remove_cvref_t<_Iter>>::value &&
|
||||
is_same<__remove_cvref_t<_Iter>, __remove_cvref_t<_Sent>>::value) {
|
||||
_LIBCPP_ASSERT_INTERNAL(std::__is_valid_range(std::__to_address(__first), std::__to_address(__last)),
|
||||
"Valid range assumption does not hold");
|
||||
if (!__libcpp_is_constant_evaluated()) {
|
||||
using __value_type = typename iterator_traits<__remove_cvref_t<_Iter>>::value_type;
|
||||
__builtin_assume_dereferenceable(std::__to_address(__first), (__last - __first) * sizeof(__value_type));
|
||||
(void)std::__assume_aligned<_LIBCPP_ALIGNOF(__value_type)>(std::__to_address(__first));
|
||||
(void)std::__assume_aligned<_LIBCPP_ALIGNOF(__value_type)>(std::__to_address(__last));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___MEMORY_VALID_RANGE_H
|
||||
|
|
@ -12,12 +12,12 @@
|
|||
#include <__algorithm/comp.h>
|
||||
#include <__assert>
|
||||
#include <__config>
|
||||
#include <__memory/valid_range.h>
|
||||
#include <__type_traits/enable_if.h>
|
||||
#include <__type_traits/integral_constant.h>
|
||||
#include <__type_traits/is_constant_evaluated.h>
|
||||
#include <__type_traits/void_t.h>
|
||||
#include <__utility/declval.h>
|
||||
#include <__utility/is_valid_range.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___UTILITY_IS_VALID_RANGE_H
|
||||
#define _LIBCPP___UTILITY_IS_VALID_RANGE_H
|
||||
|
||||
#include <__algorithm/comp.h>
|
||||
#include <__config>
|
||||
#include <__type_traits/is_constant_evaluated.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") bool
|
||||
__is_valid_range(const _Tp* __first, const _Tp* __last) {
|
||||
if (__libcpp_is_constant_evaluated()) {
|
||||
// If this is not a constant during constant evaluation, that is because __first and __last are not
|
||||
// part of the same allocation. If they are part of the same allocation, we must still make sure they
|
||||
// are ordered properly.
|
||||
return __builtin_constant_p(__first <= __last) && __first <= __last;
|
||||
}
|
||||
|
||||
return !__less<>()(__last, __first);
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___UTILITY_IS_VALID_RANGE_H
|
||||
|
|
@ -1690,6 +1690,7 @@ module std [system] {
|
|||
}
|
||||
module uses_allocator { header "__memory/uses_allocator.h" }
|
||||
module uses_allocator_construction { header "__memory/uses_allocator_construction.h" }
|
||||
module valid_range { header "__memory/valid_range.h" }
|
||||
|
||||
header "memory"
|
||||
export *
|
||||
|
|
@ -2172,7 +2173,6 @@ module std [system] {
|
|||
}
|
||||
module integer_sequence { header "__utility/integer_sequence.h" }
|
||||
module is_pointer_in_range { header "__utility/is_pointer_in_range.h" }
|
||||
module is_valid_range { header "__utility/is_valid_range.h" }
|
||||
module lazy_synth_three_way_comparator { header "__utility/lazy_synth_three_way_comparator.h" }
|
||||
module move { header "__utility/move.h" }
|
||||
module no_destroy { header "__utility/no_destroy.h" }
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ protected:
|
|||
# include <__assert>
|
||||
# include <__fwd/streambuf.h>
|
||||
# include <__locale>
|
||||
# include <__memory/valid_range.h>
|
||||
# include <__type_traits/is_same.h>
|
||||
# include <__utility/is_valid_range.h>
|
||||
# include <__utility/scope_guard.h>
|
||||
# include <climits>
|
||||
# include <ios>
|
||||
|
|
|
|||
|
|
@ -22,30 +22,18 @@
|
|||
int main(int argc, char** argv) {
|
||||
auto std_find = [](auto first, auto last, auto const& value) { return std::find(first, last, value); };
|
||||
auto std_find_if = [](auto first, auto last, auto const& value) {
|
||||
return std::find_if(first, last, [&](auto element) {
|
||||
benchmark::DoNotOptimize(element);
|
||||
return element == value;
|
||||
});
|
||||
return std::find_if(first, last, [&](auto element) { return element == value; });
|
||||
};
|
||||
auto std_find_if_not = [](auto first, auto last, auto const& value) {
|
||||
return std::find_if_not(first, last, [&](auto element) {
|
||||
benchmark::DoNotOptimize(element);
|
||||
return element != value;
|
||||
});
|
||||
return std::find_if_not(first, last, [&](auto element) { return element != value; });
|
||||
};
|
||||
|
||||
auto ranges_find = [](auto first, auto last, auto const& value) { return std::ranges::find(first, last, value); };
|
||||
auto ranges_find_if = [](auto first, auto last, auto const& value) {
|
||||
return std::ranges::find_if(first, last, [&](auto element) {
|
||||
benchmark::DoNotOptimize(element);
|
||||
return element == value;
|
||||
});
|
||||
return std::ranges::find_if(first, last, [&](auto element) { return element == value; });
|
||||
};
|
||||
auto ranges_find_if_not = [](auto first, auto last, auto const& value) {
|
||||
return std::ranges::find_if_not(first, last, [&](auto element) {
|
||||
benchmark::DoNotOptimize(element);
|
||||
return element != value;
|
||||
});
|
||||
return std::ranges::find_if_not(first, last, [&](auto element) { return element != value; });
|
||||
};
|
||||
|
||||
auto register_benchmarks = [&](auto bm, std::string comment) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <__utility/is_valid_range.h>
|
||||
#include <__memory/valid_range.h>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ TEST_CONSTEXPR_CXX17 bool test() {
|
|||
test_iter<forward_iterator<int*> >();
|
||||
test_iter<bidirectional_iterator<int*> >();
|
||||
test_iter<random_access_iterator<int*> >();
|
||||
|
||||
{ // Check that [nullptr, nullptr) is an accepted range
|
||||
auto result = std::find_if(static_cast<int*>(nullptr), static_cast<int*>(nullptr), [](int i) { return i == 0; });
|
||||
assert(result == nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue