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

            Bug ID: 127808
           Summary: [SPARC] DEBUG_INSN hides BARRIER in relax_delay_slots
                    and changes delay-slot decisions
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: yqtian668 at gmail dot com
  Target Milestone: ---
              Host: x86_64-pc-linux-gnu
            Target: sparc-elf (also reproduced on sh-elf and mips-elf)
             Build: x86_64-pc-linux-gnu

Created attachment 65796
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65796&action=edit
patch

While following up Jeff Law's review of the patch for PR
rtl-optimization/127795, I checked the BARRIER queries he pointed out in
relax_delay_slots. Jeff noted that these checks could potentially cause a
-fcompare-debug difference if a DEBUG_INSN appeared between a BARRIER and a
CODE_LABEL. I also found the potential issue before submitting that patch, but
I failed to find an concrete bug triggering input, and thus I initially replied
that I had not seen GCC produce that position. 

This report corrects that reply: pass_cleanup_barriers can create exactly that
sequence, and both queries can change behavior.

This reproduces with GCC trunk at revision
a02e2004cb707155d5b9b0a82dd428ded0ee2f6f using a sparc-elf cross compiler.

Reproducer A:

void
foo (int b, int c)
{
  void *x = &&lab;
  if (b < 5)
    goto l2;
l1:
  if (c < 0)
    goto l1;
l2:
  if (b)
    {
    lab:
      __builtin_unreachable ();
    }
  if (c)
    x = &&l1;
  goto *x;
}

Compile with:

  sparc-elf-gcc -O2 -g -fno-var-tracking -fcompare-debug -S site-a.c

Reproducer B:

int h;

void
foo (int b, int c)
{
  int i = b * 3;
  void *x = &&lab;
  if (b == 3)
    goto l2;
lab:
  __builtin_unreachable ();
l1:
  while (b < i)
    h = c;
l2:
  if (c)
    x = &&l1;
  goto *x;
}

Compile with:

  sparc-elf-gcc -O1 -g -fno-var-tracking -fcompare-debug -S site-b.c

Both commands report a compare-debug failure. At the first site, the -g RTL
keeps a stale BARRIER in fall-through code. At the second site, a filled jump
is deleted only with -g. The ordinary code-generation decision therefore
depends on the presence of a debug instruction.

The relevant sequence at dbr entry is:

  BARRIER; NOTE; DEBUG_INSN; CODE_LABEL

cleanup_barriers moves the BARRIER after the preceding non-debug instruction,
in front of the block's debug instructions. Variable tracking normally converts
these markers to notes before dbr, which is why the reproducer uses
-fno-var-tracking.

Root cause:

relax_delay_slots uses prev_nonnote_insn when checking for a BARRIER before a
target label at two sites. A DEBUG_INSN hides the BARRIER. Using
prev_nonnote_nondebug_insn at both sites makes the decision independent of
debug information.

Testing of the candidate fix:

* Two targeted gcc.target/sparc tests fail before the fix and pass after it.
* A complete sparc-elf check-gcc comparison has no new unexpected results.
* An x86_64-pc-linux-gnu native bootstrap and complete make -k check, with and
without the patch at the revision above, have identical test outcomes after
normalizing build paths.

This is a sibling of PR rtl-optimization/127795 and was found after following
Jeff's review suggestion there. A related issue in
jump.cc::delete_related_insns was found during the same audit and will be
reported separately.

I will submit the patch soon.

Reply via email to