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

            Bug ID: 127317
           Summary: [x86-64] the rewritten guard of an inlined safe-math
                    helper re-loads the operand from memory instead of
                    testing the register — same global read twice under
                    -O2/-O3, regression since gcc-15
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Keywords: missed-optimization
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: 220255624 at seu dot edu.cn
                CC: jianhao.xu at seu dot edu.cn
  Target Milestone: ---

Hi, I found such a case:

    #include <stdint.h>
    int8_t g = 7;

    __attribute__((noinline)) int func_guard(int8_t y)
    {
        int8_t a = (int8_t)(0x85 ^ g);
        if (y == 0 || (a == -128 && y == -1)) return (int)a | 8;
        return (int)(int8_t)(a / y) | 8;
    }

  Compile: gcc -O2 -S t.c      (Live:  https://gcc.godbolt.org/z/98vYP53fe)

  `a == -128 && y == -1` is the guard gcc generates for inlined safe_div /
  safe_mod helpers (the case was originally found in csmith-generated code
  using safe_mod_func_int8_t_s_s).  gcc 16.2 and 15.2 would produce under -O2:

    func_guard:
        movzbl g(%rip),%eax        # first load of g
        xor    $0xffffff85,%eax    # a = g ^ 0x85, now in %al
        test   %dil,%dil
        je     .L
        cmpb   $0x5,g(%rip)        # second load of the same byte: the
rewritten
        jne    .L                  # guard (g == 5) is a memory compare
        cmp    $0xff,%dil
        je     .L
        movsbl %al,%eax            # a is used again right here
        ...

  and gcc 14.2 (also 16.2 with -O1):

    func_guard:
        movzbl g(%rip),%edx
        mov    %edx,%eax
        xor    $0xffffff85,%eax
        test   %dil,%dil
        je     .L
        cmp    $0x5,%dl            # the value stays in a register
        ...

  I guess the latter is better?  Rewriting `(g ^ 0x85) == 0x80` into `g == 5`
  is fine, but the rewritten compare then re-reads g from memory even though
  the byte is already in a register and is used again two instructions later
  (testing `a == -128` as `cmpb $0x80,%al` would need no load at all).
  With this testcase gcc 14.2 is clean at -O2 and -O3, gcc 15.2 and 16.2 are
  not, so it looks like a regression in how the comparison is materialized.

Reply via email to