-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc++] P2590R2: Explicit lifetime management #107206
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-libcxx Author: Shivam (phyBrackets) ChangesImplementing #105234 Full diff: https://github.com/llvm/llvm-project/pull/107206.diff 5 Files Affected:
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8e..629742cbaee764 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -542,6 +542,8 @@ set(files
__memory/ranges_uninitialized_algorithms.h
__memory/raw_storage_iterator.h
__memory/shared_ptr.h
+ __memory/start_lifetime_as_array.h
+ __memory/start_lifetime_as.h
__memory/swap_allocator.h
__memory/temp_value.h
__memory/temporary_buffer.h
diff --git a/libcxx/include/__memory/start_lifetime_as.h b/libcxx/include/__memory/start_lifetime_as.h
new file mode 100644
index 00000000000000..f661a709c06c42
--- /dev/null
+++ b/libcxx/include/__memory/start_lifetime_as.h
@@ -0,0 +1,50 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_START_LIFETIME_AS_H
+#define _LIBCPP___MEMORY_START_LIFETIME_AS_H
+
+#include <__config>
+#include <__type_traits/is_array.h>
+#include <__type_traits/remove_all_extents.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct _LIBCPP_TEMPLATE_VIS __start_lifetime_as_impl {
+ _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR _Tp* __call(void* __p) _NOEXCEPT { return static_cast<_Tp*>(__p); }
+};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* start_lifetime_as(void* __p) _NOEXCEPT {
+ return __start_lifetime_as_impl<_Tp>::__call(__p);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const _Tp* start_lifetime_as(const void* __p) _NOEXCEPT {
+ return start_lifetime_as<_Tp>(const_cast<void*>(__p));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR volatile _Tp* start_lifetime_as(volatile void* __p) _NOEXCEPT {
+ return start_lifetime_as<_Tp>(const_cast<void*>(__p));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const volatile _Tp* start_lifetime_as(const volatile void* __p) _NOEXCEPT {
+ return start_lifetime_as<_Tp>(const_cast<void*>(__p));
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_START_LIFETIME_AS_H
diff --git a/libcxx/include/__memory/start_lifetime_as_array.h b/libcxx/include/__memory/start_lifetime_as_array.h
new file mode 100644
index 00000000000000..5b227870f7ed6c
--- /dev/null
+++ b/libcxx/include/__memory/start_lifetime_as_array.h
@@ -0,0 +1,47 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_START_LIFETIME_AS_ARRAY_H
+#define _LIBCPP___MEMORY_START_LIFETIME_AS_ARRAY_H
+
+#include <__config>
+#include <__memory/start_lifetime_as.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* start_lifetime_as_array(void* __p, [[maybe_unused]] size_t __n) _NOEXCEPT {
+ return static_cast<_Tp*>(__p);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const _Tp* start_lifetime_as_array(const void* __p, size_t __n) _NOEXCEPT {
+ return start_lifetime_as_array<_Tp>(const_cast<void*>(__p), __n);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR volatile _Tp*
+start_lifetime_as_array(volatile void* __p, size_t __n) _NOEXCEPT {
+ return start_lifetime_as_array<_Tp>(const_cast<void*>(__p), __n);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const volatile _Tp*
+start_lifetime_as_array(const volatile void* __p, size_t __n) _NOEXCEPT {
+ return start_lifetime_as_array<_Tp>(const_cast<void*>(__p), __n);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_START_LIFETIME_AS_ARRAY_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index b940a32c3ebe6c..3162bbb2b906a9 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -946,6 +946,8 @@ template<class Pointer = void, class Smart, class... Args>
#include <__memory/pointer_traits.h>
#include <__memory/raw_storage_iterator.h>
#include <__memory/shared_ptr.h>
+#include <__memory/start_lifetime_as.h>
+#include <__memory/start_lifetime_as_array.h>
#include <__memory/temporary_buffer.h>
#include <__memory/uninitialized_algorithms.h>
#include <__memory/unique_ptr.h>
diff --git a/libcxx/test/std/utilities/memory/default.allocator/start_lifetime_as.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/start_lifetime_as.pass.cpp
new file mode 100644
index 00000000000000..e483516e77b46d
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/default.allocator/start_lifetime_as.pass.cpp
@@ -0,0 +1,127 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// template <class T>
+// T* start_lifetime_as(void* p) noexcept;
+//
+// template <class T>
+// const T* start_lifetime_as(const void* p) noexcept;
+//
+// template <class T>
+// volatile T* start_lifetime_as(volatile void* p) noexcept;
+//
+// template <class T>
+// const volatile T* start_lifetime_as(const volatile void* p) noexcept;
+//
+// template <class T>
+// T* start_lifetime_as_array(void* p, size_t n) noexcept;
+//
+// template <class T>
+// const T* start_lifetime_as_array(const void* p, size_t n) noexcept;
+//
+// template <class T>
+// volatile T* start_lifetime_as_array(volatile void* p, size_t n) noexcept;
+//
+// template <class T>
+// const volatile T* start_lifetime_as_array(const volatile void* p, size_t n) noexcept;
+
+#include <memory>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+struct S {
+ int x;
+ double y;
+};
+
+template <class T>
+void test_start_lifetime_as() {
+ alignas(T) char buffer[sizeof(T)];
+
+ {
+ T* ptr = std::start_lifetime_as<T>(buffer);
+ ASSERT_SAME_TYPE(decltype(ptr), T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as<T>(buffer));
+ assert(ptr == reinterpret_cast<T*>(buffer));
+ }
+
+ {
+ const T* cptr = std::start_lifetime_as<T>(static_cast<const void*>(buffer));
+ ASSERT_SAME_TYPE(decltype(cptr), const T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as<T>(static_cast<const void*>(buffer)));
+ assert(cptr == reinterpret_cast<const T*>(buffer));
+ }
+
+ {
+ volatile T* vptr = std::start_lifetime_as<T>(static_cast<volatile void*>(buffer));
+ ASSERT_SAME_TYPE(decltype(vptr), volatile T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as<T>(static_cast<volatile void*>(buffer)));
+ assert(vptr == reinterpret_cast<volatile T*>(buffer));
+ }
+
+ {
+ const volatile T* cvptr = std::start_lifetime_as<T>(static_cast<const volatile void*>(buffer));
+ ASSERT_SAME_TYPE(decltype(cvptr), const volatile T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as<T>(static_cast<const volatile void*>(buffer)));
+ assert(cvptr == reinterpret_cast<const volatile T*>(buffer));
+ }
+}
+
+template <class T>
+void test_start_lifetime_as_array() {
+ constexpr size_t count = 5;
+ alignas(T) char buffer[sizeof(T) * count];
+
+ {
+ T* ptr = std::start_lifetime_as_array<T>(buffer, count);
+ ASSERT_SAME_TYPE(decltype(ptr), T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as_array<T>(buffer, count));
+ assert(ptr == reinterpret_cast<T*>(buffer));
+ }
+
+ {
+ const T* cptr = std::start_lifetime_as_array<T>(static_cast<const void*>(buffer), count);
+ ASSERT_SAME_TYPE(decltype(cptr), const T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as_array<T>(static_cast<const void*>(buffer), count));
+ assert(cptr == reinterpret_cast<const T*>(buffer));
+ }
+
+ {
+ volatile T* vptr = std::start_lifetime_as_array<T>(static_cast<volatile void*>(buffer), count);
+ ASSERT_SAME_TYPE(decltype(vptr), volatile T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as_array<T>(static_cast<volatile void*>(buffer), count));
+ assert(vptr == reinterpret_cast<volatile T*>(buffer));
+ }
+
+ {
+ const volatile T* cvptr = std::start_lifetime_as_array<T>(static_cast<const volatile void*>(buffer), count);
+ ASSERT_SAME_TYPE(decltype(cvptr), const volatile T*);
+ ASSERT_NOEXCEPT(std::start_lifetime_as_array<T>(static_cast<const volatile void*>(buffer), count));
+ assert(cvptr == reinterpret_cast<const volatile T*>(buffer));
+ }
+}
+
+int main(int, char**) {
+ test_start_lifetime_as<char>();
+ test_start_lifetime_as<int>();
+ test_start_lifetime_as<double>();
+ test_start_lifetime_as<S>();
+
+ test_start_lifetime_as_array<char>();
+ test_start_lifetime_as_array<int>();
+ test_start_lifetime_as_array<double>();
+ test_start_lifetime_as_array<S>();
+
+ return 0;
+}
\ No newline at end of file
|
|
||
template <class _Tp> | ||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* start_lifetime_as_array(void* __p, [[maybe_unused]] size_t __n) _NOEXCEPT { | ||
return static_cast<_Tp*>(__p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No built-in used here. Could you explain why this is considered to work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function itself doesn't actually do anything at runtime. It's a no-op in terms of generated code. It happens at the compiler level. When the compiler sees a call to this function, it treats it as a signal that it should consider the lifetime of an array of _Tp
objects to have begun at the memory location pointed to by __p
, even though no constructors have been called. Or is it intended something else? I am not sure, Could you explain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the compiler sees a call to this function,
Currently, I'm not seeing anything in the source code of Clang or GCC that recognizes start_lifetime_as(_array)
. So I can't believe this approach will automatically work,
Or is it intended something else? I am not sure, Could you explain?
I guess an intrinsic like __builitin_start_lifetime_as
(or __builtin_tbaa_barrier
?) is expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah Okay, that make sense, is it worth it if we currently define a macro to check if the compiler supports the necessary intrinsics?
If supported, it declares the intrinsics and implements start_lifetime_as
and start_lifetime_as_array
using these intrinsics. If not supported, it falls back to the current implementation (which is essentially a no-op).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's a bad idea, since people expect things to work as advertised that exist. IOW, these functions should only exist if we can actually implement them.
"implementing" is not a valid GitHub "keyword", please check: https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue. You should use e.g. Also if I recall correctly you'll need this PR to be merged first: #106870 |
This requires compiler support to inject aliasing barriers. There was an attempt here to support the non-array case https://reviews.llvm.org/D147020 @davidtgoldblatt @sam-mccall @ilya-biryukov (See also an attempt to hijack launder-semantics for that, which we think is insufficient #82776 ) Given that all of this is only observable in complex use cases, i think it's prudent not to try to implement something that would not provide the same guarantees as the standard |
Implementing #105234