On Wed, 4 Mar 2026 at 14:46, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Wed, Mar 4, 2026 at 12:56 PM Jonathan Wakely <[email protected]> wrote:
>>
>> As Bug 122300 shows, we have at least one target where the
>> static_assert added by r16-4422-g1b18a9e53960f3 fails. This patch
>> resurrects the original proposal for using aligned new that I posted in
>> https://gcc.gnu.org/pipermail/libstdc++/2025-October/063904.html
>>
>> Instead of just asserting that the memory from operator new will be
>> sufficiently aligned, check whether it will be and use aligned new if
>> needed. We don't just use aligned new unconditionally, because that can
>> add overhead on targets where malloc already meets the requirements.
>>
>> libstdc++-v3/ChangeLog:
>>
>> PR libstdc++/122300
>> * src/c++17/fs_path.cc (path::_List::_Impl): Remove
>> static_asserts.
>> (path::_List::_Impl::required_alignment)
>> (path::_List::_Impl::use_aligned_new): New static data members.
>> (path::_List::_Impl::create_unchecked): Check use_aligned_new
>> and use aligned new if needed.
>> (path::_List::_Impl::alloc_size): New static member function.
>> (path::_List::_Impl_deleter::operator): Check use_aligned_new
>> and use aligned delete if needed.
>> ---
>>
>> Tested x86_64-linux and bootstrapped pru-unknown-elf, confirming that it
>> fixes the failed static_assert during bootstrap.
>
> Looks good. Remember discussing these changes with the original patch?
> we didn't knew if any target was affected.
Yes, so we simplified it to just using a static_assert.
You also said you'd prefer to use aligned-new unconditionally, but I
still think we should avoid the possible extra overhead of
aligned_alloc and only do that when malloc isn't OK for the type.
>>
>>
>> libstdc++-v3/src/c++17/fs_path.cc | 27 +++++++++++++++++++++------
>> 1 file changed, 21 insertions(+), 6 deletions(-)
>>
>> diff --git a/libstdc++-v3/src/c++17/fs_path.cc
>> b/libstdc++-v3/src/c++17/fs_path.cc
>> index 9d8d38266789..c217dc278018 100644
>> --- a/libstdc++-v3/src/c++17/fs_path.cc
>> +++ b/libstdc++-v3/src/c++17/fs_path.cc
>> @@ -260,9 +260,12 @@ struct path::_List::_Impl
>>
>> // We use the two least significant bits to store a _Type value so
>> // require memory aligned to at least 4 bytes:
>> - static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= 4);
>> - // Require memory suitably aligned for an _Impl and its value types:
>> - static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(value_type));
>> + static constexpr size_t required_alignment
>> + = std::max(size_t(4), alignof(value_type));
>> +
>> + // Only use aligned new if needed, because it might be less efficient.
>> + static constexpr bool use_aligned_new
>> + = __STDCPP_DEFAULT_NEW_ALIGNMENT__ < required_alignment;
>>
>> // Clear the lowest two bits from the pointer (i.e. remove the _Type
>> value)
>> static _Impl* notype(_Impl* p)
>> @@ -297,9 +300,18 @@ struct path::_List::_Impl
>> static unique_ptr<_Impl, _Impl_deleter>
>> create_unchecked(int n)
>> {
>> - void* p = ::operator new(sizeof(_Impl) + n * sizeof(value_type));
>> + const auto bytes = alloc_size(n);
>> + void* p;
>> + if constexpr (use_aligned_new)
>> + p = ::operator new(bytes, align_val_t{required_alignment});
>> + else
>> + p = ::operator new(bytes);
>> return std::unique_ptr<_Impl, _Impl_deleter>(::new(p) _Impl{n});
>> }
>> +
>> + // The number of bytes that must be allocated for _Impl with capacity n.
>> + static size_t
>> + alloc_size(int n) { return sizeof(_Impl) + n * sizeof(value_type); }
>> };
>>
>> // Destroy and deallocate an _Impl.
>> @@ -309,9 +321,12 @@ path::_List::_Impl_deleter::operator()(_Impl* p) const
>> noexcept
>> p = _Impl::notype(p);
>> if (p)
>> {
>> - const auto n = p->_M_capacity;
>> + const auto bytes = _Impl::alloc_size(p->_M_capacity);
>> p->~_Impl();
>> - ::operator delete(p, sizeof(_Impl) + n * sizeof(_Impl::value_type));
>> + if constexpr (_Impl::use_aligned_new)
>> + ::operator delete(p, bytes, align_val_t{_Impl::required_alignment});
>> + else
>> + ::operator delete(p, bytes);
>> }
>> }
>>
>> --
>> 2.53.0
>>