https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128440
Bug ID: 128440
Summary: [14/15/16/17 Regression] BPF incorrectly expands
backwards memmove
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: gabravier at gmail dot com
Target Milestone: ---
long buf[4];
void f (void) { __builtin_memmove (&buf[1], &buf[0], 16); }
With -O2, this code is compiled by GCC trunk to do this:
f:
r0 = buf+8 ll
r1 = buf ll
if r1 >= r0 goto .L2
r2 = *(u64 *) (r1+15)
r3 = *(u64 *) (r1+7)
*(u64 *) (r0+15) = r2
*(u64 *) (r0+7) = r3
exit
.L2:
r4 = *(u64 *) (r1+0)
*(u64 *) (r0+0) = r4
r5 = *(u64 *) (r1+8)
*(u64 *) (r0+8) = r5
exit
...which ends up actually doing `memmove ((char *) buf + 15, (char *) buf + 7,
16);`.
GCC 16 also emits the same, and 15 and 14 instead implement `memmove(&buf[2],
&buf[1], 16)`, which is also wrong. GCC 13 emits theoretically correct code by
simply calling memmove.
I would thus suspect the code in bpf_expand_cpymem/emit_move_loop is wrong in
this regard - in particular:
if (is_move)
{
/* For memmove, be careful of overlap. It is not a concern for memcpy.
To handle overlap, we check (at runtime) if SRC < DST, and if so do
the move "backwards" starting from SRC + SIZE. */
fwd_label = gen_label_rtx ();
done_label = gen_label_rtx ();
rtx src_addr = force_operand (XEXP (src, 0), NULL_RTX);
rtx dst_addr = force_operand (XEXP (dst, 0), NULL_RTX);
emit_cmp_and_jump_insns (src_addr, dst_addr, GEU, NULL_RTX, Pmode,
true, fwd_label, profile_probability::even ());
/* Emit the "backwards" unrolled loop. */
emit_move_loop (src, dst, mode, (size_bytes - 1), -inc, iters,
remainder);
emit_jump_insn (gen_jump (done_label));
emit_barrier ();
emit_label (fwd_label);
}
...appears to be only correct for the backwards loop where `size_bytes` is 1 -
I would assume `(size_bytes - 1)` was intended to be `(size_bytes - inc)`
(although, I have not actually tested this - and i'm not certain it's the only
problem potentially?)
P.S.: I originally found the bug with this code:
void g (void *d, void *s)
{ __builtin_memmove (__builtin_assume_aligned (d, 4),
__builtin_assume_aligned (s, 4), 6); }
...but the first example seems simpler to understand :)