https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108846

--- Comment #22 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #16)
>           const ptrdiff_t _Num = __last - __first;
> -         if (_Num)
> +         if (__builtin_expect(_Num > 1, true))
>             __builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
> +         else if (_Num == 1)

Richi suggested that we could avoid these runtime branches (which hurt
optimization, see PR 109445) if we knew how many bytes of tail padding there
are in _Tp, e.g.,

  __builtin_memmove(__result, __first, sizeof(_Tp) * _Num - __padding_bytes);

This would mean we copy whole objects for the first Num - 1 elements, and then
only copy the non-padding bytes for the last element. This would be conforming,
because there's no guarantee that padding bits are copied by assignment anyway,
and std::copy is specified in terms of assignment.

When copying a single base-class object (the subject of this bug) the last
element is the only element, so we solve the problem.

We don't have a built-in to tell us the number of padding bytes, but we might
be able to use something like this:

template<typename T>
constexpr size_t
potentially_overlapping_tail_padding()
{
  if constexpr (is_final<T>::value)
    return 0;
  else
  {
    struct D1 : T
    {
      char c1;
    };
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
    return sizeof(T) - offsetof(D1, c1);
#pragma GCC diagnostic pop
  }
}

For pre-C++17 we would need to use tag dispatching and __is_final instead of
if-constexpr, but that's doable. For pre-C++14 it can't be constexpr, but
that's OK too.

Reply via email to