Re: [PATCH 10/20] alpha: add EV6/EV7 assembly memmove

Magnus Lindholm <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <CA+=Fv5SvVDai_uXXC6mr0jUbanRrk95hBzu-_Caoy8M0i7mN9w@mail.gmail.com>
On Wed, Aug 12, 2026 at 3:20 AM Matt Turner <[email protected]> wrote:
>
> Alpha used the generic C memmove.  Add an assembly one built out of the ev6
> memcpy: when the destination does not overlap the source from below -- that
> is, (uintptr) dst - (uintptr) src >= n, which also covers dst < src -- the
> copy runs forward and is exactly memcpy, with the same-alignment quadword
> fast path, the wh64 unrolled loop on the 21264 and the prefetch loop on the
> 21364, and the rotating ldq_u path for mismatched alignment.  Only the
> genuinely overlapping case (src < dst < src+n) copies backward, and that is
> rare and not performance critical, so it uses a straightforward descending
> aligned-quadword-or-byte loop.
>
> Two details of the forward path are specific to memmove and are not
> inherited from memcpy, because memcpy never sees an overlapping copy.
>
> First, wh64 claims a cache line for writing without filling it from memory,
> which makes the whole aligned 64-byte block UNPREDICTABLE unless every byte
> of it is stored.  In a forward overlapping copy the hint runs ahead of the
> stores, so it can land on source bytes that have not been read yet and
> destroy them before the copy consumes them.  At trip k the hint covers
> [dst_k+128, dst_k+192) and the unread source begins at dst_k + (src-dst),
> so the copy is safe exactly when src-dst >= 192.  Below that the copy takes
> the prefetch loop instead, which substitutes LDS to F31 for the wh64 -- a
> prefetch with modify intent that only reads, and so cannot destroy the
> source -- and is correct at any overlap because it reads a whole block into
> registers before storing any of it.  Note that bound tracks the wh64
> distance: it is the hint's far edge, so it has to move whenever that moves.
>
> Second, the guard sends a too-small gap to the 64-byte prefetch loop rather
> than to the scalar tail loop, which would cost roughly 8x on exactly the
> copies it catches.  A Python list.remove() moves its ob_item array with a
> gap of a single pointer, so it always takes this path.
>
> Verified on an EV68CB against the C memmove over every size class from 0 to
> 64KB, at 72 destination alignments for the non-overlapping case and across
> source-destination gaps from 8 bytes to 1KB for the overlapping one, with
> 512-byte guard regions checked for stray writes.
> ---

Hi Matt,

Would it make sense to use the same approach as patch 11, where
mempcpy is built from memcpy.S with a USE_AS_MEMPCPY define, and do
something similar for the forward memmove path? That would keep the
EV6/EV7 dispatch, bulk loop and wh64 scheduling in one place, so
changes like 15/20 automatically apply to memmove as well.

I realize memmove needs more conditional handling because of the
overlap test, backward path and the wh64 overlap guard, so it may make
memcpy.S less tidy, Maybe keeping separate files is preferable...

Magnus
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.