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 :)

Reply via email to