From: Jonathan Wakely Date: Tue, 5 May 2026 12:39:24 +0000 (+0100) Subject: libstdc++: Replace uses of EBO with [[no_unique_address]] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c30bf3aee78bbe9bd557c7bcd5d2d5533f6a216e;p=thirdparty%2Fgcc.git libstdc++: Replace uses of EBO with [[no_unique_address]] Clang 9 added support for [[__no_unique_address__]] and we don't support Intel icc any longer, so we can remove the code in that works around the absence of that attribute. We can also address a FIXME in and replace uses of EBO with the attribute. libstdc++-v3/ChangeLog: * include/bits/shared_ptr_base.h (_Sp_ebo_helper): Simplify by using [[__no_unique_address__]] instead of EBO. Use the attribute unconditionally for the unstable ABI. (_Sp_counted_deleter::_Impl): Adjust uses of _Sp_ebo_helper. (_Sp_counted_ptr_inplace::_Impl): Likewise. * include/std/tuple (_Head_base): Remove implementation for compilers that don't support [[__no_unique_address__]]. Use the attribute unconditionally for the unstable ABI. Reviewed-by: Tomasz KamiƄski --- diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h index cfaee6df37b..4a8c6770399 100644 --- a/libstdc++-v3/include/bits/shared_ptr_base.h +++ b/libstdc++-v3/include/bits/shared_ptr_base.h @@ -513,57 +513,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline void _Sp_counted_ptr::_M_dispose() noexcept { } - // FIXME: once __has_cpp_attribute(__no_unique_address__)) is true for - // all supported compilers we can greatly simplify _Sp_ebo_helper. +#if ! __has_cpp_attribute(__no_unique_address__) +#error "support for [[__no_unique_address__]] attribute is required" +#endif + +#if ! _GLIBCXX_INLINE_VERSION // N.B. unconditionally applying the attribute could change layout for // final types, which currently cannot use EBO so have a unique address. - - template + template struct _Sp_ebo_helper; +#else + template + struct _Sp_ebo_helper; +#endif - /// Specialization using EBO. - template - struct _Sp_ebo_helper<_Nm, _Tp, true> : private _Tp + /// Specialization using [[no_unique_address]]. + template + struct _Sp_ebo_helper<_Tp, true> { - explicit _Sp_ebo_helper(const _Tp& __tp) : _Tp(__tp) { } - explicit _Sp_ebo_helper(_Tp&& __tp) : _Tp(std::move(__tp)) { } - - static _Tp& - _S_get(_Sp_ebo_helper& __eboh) { return static_cast<_Tp&>(__eboh); } + [[__no_unique_address__]] _Tp _M_obj; }; - /// Specialization not using EBO. - template - struct _Sp_ebo_helper<_Nm, _Tp, false> +#if ! _GLIBCXX_INLINE_VERSION + /// Specialization not using [[no_unique_address]]. + template + struct _Sp_ebo_helper<_Tp, false> { - explicit _Sp_ebo_helper(const _Tp& __tp) : _M_tp(__tp) { } - explicit _Sp_ebo_helper(_Tp&& __tp) : _M_tp(std::move(__tp)) { } - - static _Tp& - _S_get(_Sp_ebo_helper& __eboh) - { return __eboh._M_tp; } - - private: - _Tp _M_tp; + _Tp _M_obj; }; +#endif // Support for custom deleter and/or allocator template class _Sp_counted_deleter final : public _Sp_counted_base<_Lp> { - class _Impl : _Sp_ebo_helper<0, _Deleter>, _Sp_ebo_helper<1, _Alloc> + class _Impl { - typedef _Sp_ebo_helper<0, _Deleter> _Del_base; - typedef _Sp_ebo_helper<1, _Alloc> _Alloc_base; + [[__no_unique_address__]] _Sp_ebo_helper<_Deleter> _M_d; + [[__no_unique_address__]] _Sp_ebo_helper<_Alloc> _M_a; public: _Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept - : _Del_base(std::move(__d)), _Alloc_base(__a), _M_ptr(__p) + : _M_d{std::move(__d)}, _M_a{__a}, _M_ptr(__p) { } - _Deleter& _M_del() noexcept { return _Del_base::_S_get(*this); } - _Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); } + _Deleter& _M_del() noexcept { return _M_d._M_obj; } + _Alloc& _M_alloc() noexcept { return _M_a._M_obj; } _Ptr _M_ptr; }; @@ -645,14 +640,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp> { - class _Impl : _Sp_ebo_helper<0, _Alloc> + class _Impl { - typedef _Sp_ebo_helper<0, _Alloc> _A_base; + [[__no_unique_address__]] _Sp_ebo_helper<_Alloc> _M_a; public: - explicit _Impl(_Alloc __a) noexcept : _A_base(__a) { } + explicit _Impl(_Alloc __a) noexcept : _M_a{std::move(__a)} { } - _Alloc& _M_alloc() noexcept { return _A_base::_S_get(*this); } + _Alloc& _M_alloc() noexcept { return _M_a._M_obj; } __gnu_cxx::__aligned_buffer<__remove_cv_t<_Tp>> _M_storage; }; diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index f7caa79cda0..cbacd5a3c97 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -68,15 +68,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template class tuple; +#if ! __has_cpp_attribute(__no_unique_address__) +#error "support for [[__no_unique_address__]] attribute is required" +#endif + /// @cond undocumented +#if ! _GLIBCXX_INLINE_VERSION template struct __is_empty_non_tuple : is_empty<_Tp> { }; // Using EBO for elements that are tuples causes ambiguous base errors. + // Although we have replaced EBO with [[no_unique_address]] we have to + // preserve the original layout for ABI compatibility. template struct __is_empty_non_tuple> : false_type { }; - // Use the Empty Base-class Optimization for empty, non-final types. + // Use [[no_unique_address]] for empty, non-final types that are not tuples. template using __empty_not_final = __conditional_t<__is_final(_Tp), false_type, @@ -85,10 +92,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template::value> struct _Head_base; +#else + // For the unstable ABI we always use [[no_unique_address]]. + template + struct _Head_base; +#endif -#if __has_cpp_attribute(__no_unique_address__) - template - struct _Head_base<_Idx, _Head, true> + // Primary template uses [[no_unique_address]]. + template + struct _Head_base { constexpr _Head_base() : _M_head_impl() { } @@ -141,61 +153,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION [[__no_unique_address__]] _Head _M_head_impl; }; -#else - template - struct _Head_base<_Idx, _Head, true> - : public _Head - { - constexpr _Head_base() - : _Head() { } - - constexpr _Head_base(const _Head& __h) - : _Head(__h) { } - - constexpr _Head_base(const _Head_base&) = default; - constexpr _Head_base(_Head_base&&) = default; - - template - constexpr _Head_base(_UHead&& __h) - : _Head(std::forward<_UHead>(__h)) { } - - _GLIBCXX20_CONSTEXPR - _Head_base(allocator_arg_t, __uses_alloc0) - : _Head() { } - - template - _GLIBCXX20_CONSTEXPR - _Head_base(allocator_arg_t, __uses_alloc1<_Alloc> __a) - : _Head(allocator_arg, *__a._M_a) { } - - template - _GLIBCXX20_CONSTEXPR - _Head_base(allocator_arg_t, __uses_alloc2<_Alloc> __a) - : _Head(*__a._M_a) { } - - template - _GLIBCXX20_CONSTEXPR - _Head_base(__uses_alloc0, _UHead&& __uhead) - : _Head(std::forward<_UHead>(__uhead)) { } - - template - _GLIBCXX20_CONSTEXPR - _Head_base(__uses_alloc1<_Alloc> __a, _UHead&& __uhead) - : _Head(allocator_arg, *__a._M_a, std::forward<_UHead>(__uhead)) { } - - template - _GLIBCXX20_CONSTEXPR - _Head_base(__uses_alloc2<_Alloc> __a, _UHead&& __uhead) - : _Head(std::forward<_UHead>(__uhead), *__a._M_a) { } - - static constexpr _Head& - _M_head(_Head_base& __b) noexcept { return __b; } - - static constexpr const _Head& - _M_head(const _Head_base& __b) noexcept { return __b; } - }; -#endif +#if ! _GLIBCXX_INLINE_VERSION + // Partial specialization that doesn't use [[no_unique_address]]. template struct _Head_base<_Idx, _Head, false> { @@ -250,6 +210,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Head _M_head_impl; }; +#endif #if __cpp_lib_tuple_like // >= C++23 struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; };