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

            Bug ID: 128484
           Summary: loop-carried signed bit-field load and its sign
                    extract stay outside the cutoff test
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: Subodh.SunilHumne at amd dot com
  Target Milestone: ---

GCC keeps a loop-carried load of a signed bit-field, and the shifts that
sign-extract it, on the path that runs on every iteration.  The only
consumer is the range test inside a cutoff branch.

Target: x86_64-linux.  Options: -O2.

  struct Ext {
    int id : 30;
    unsigned atomFixed : 1;
    unsigned groupFixed : 1;
  };

  __attribute__((noinline))
  int filter (const struct Ext *ext, const double *r2, int n,
              double cutoff, int lo, int hi, int *out)
  {
    int j = 0;
    int id = ext[0].id;
    for (int i = 0; i < n; ++i)
      {
        if (r2[i] < cutoff)
          {
            if (id >= lo && id <= hi)
              out[j++] = i;
          }
        id = ext[i + 1].id;
      }
    return j;
  }

The value tested on this iteration was loaded at the end of the previous
one.  id = ext[i + 1].id is outside the cutoff, so a rejected pair still
loads and sign-extracts the next id.

GIMPLE carries the widened int.  The load and the conversion sit in the
block that both the passing and failing edges reach:

  <bb 3>:
    # id_41 = PHI <id_33(latch), id_24(preheader)>
    if (r2[i] < cutoff)
      goto <bb 4>;          // only use of id_41

  <bb latch>:
    _15 = ext[i + 1].id;    // <unnamed-signed:30>
    id_33 = (int) _15;

The (int) conversion is not where the x86 shifts come from.  The 30-bit
temporary and int both use SImode, so the cast expands to a copy.
extract_bit_field sign-extracts while expanding the COMPONENT_REF, which
is sall $2 / sarl $2.  Sinking the cast leaves the shifts on the load.
The phi is the widened int rather than the address, so there is no
pointer to reload under the cutoff.  pass_sink_code runs on GIMPLE, and
no RTL pass moves the load and the extract under the branch.

x86-64 assembly of the loop, loads and shifts on the latch:

        comisd  (%rsi,%rcx,8), %xmm0
        jbe     .L7
        cmpl    %eax, %r11d
        ...
  .L7:
        movl    (%rdi), %eax
        addq    $1, %rcx
        addq    $4, %rdi
        sall    $2, %eax
        sarl    $2, %eax
        jne     .L8

Desired placement: carry the address, and reload and sign-extract only
after the cutoff succeeds.

        comisd  (%rsi,%rcx,8), %xmm0
        jbe     .L7
        movl    (%rdi), %eax
        sall    $2, %eax
        sarl    $2, %eax
        cmpl    %eax, %lo
        ...
  .L7:
        addq    $4, %rdi          # next address only

Reply via email to