[Bug tree-optimization/91663] New: split function can be re-inlined, leaving bad stack trace

ian at airs dot com gcc-bugzilla@gcc.gnu.org
Thu Sep 5 01:01:00 GMT 2019


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

            Bug ID: 91663
           Summary: split function can be re-inlined, leaving bad stack
                    trace
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ian at airs dot com
  Target Milestone: ---

Test case (this is C code converted from a Go program):

extern void exit (int) __attribute ((__noreturn__));

struct s
{
  int f1;
  unsigned int f2;
};

void
i1 (void *p)
{
}

void quit (const char *) __attribute ((__noreturn__, __noinline__));

void
quit (const char *s)
{
  exit (1);
}

#define c0 1
#define c1 2
#define c2 4

#define c3 3

static void f3 (unsigned int *, int, int) __attribute__ ((__noinline__));

static void
f3 (unsigned int *p, int v, int v2)
{
  quit ("f3");
}

void
f2 (struct s *m, int v)
{
  if (((v + c0) & c0) == 0)
      quit ("fail1");
  if ((v & c2) == 0)
    {
      int v2 = v;
    lab:
        {
          _Bool f = (v2 >> c3) == 0;
          if (! f)
            f = (v2 & (c0 | c1 | c2)) != 0;
          if (f)
            return;
          v = (v2 - (1 << c3)) | c1;
          if (__atomic_compare_exchange_n (&m->f1, &v2, v, 0,
                                           __ATOMIC_SEQ_CST,
                                           __ATOMIC_SEQ_CST) != 0)
            {
              f3 (&m->f2, 0, 1);
              return;
            }
          v2 = m->f1;
        }
        goto lab;
    }
  else
    f3 (&m->f2, 1, 1);
}

void f1 (struct s *) __attribute__ ((__noinline__));

void
f1 (struct s *m)
{
  int v;

  if (0)
    i1 ((void*) m);
  v = __atomic_add_fetch (&m->f1, -c0, __ATOMIC_SEQ_CST);
  if (v != 0)
    f2 (m, v);
}

int
main ()
{
  struct s m;
  m.f1 = c0 + (1 << c3);
  m.f2 = 0;
  f1 (&m);
}


> gcc -o foo -g -O2 -fno-optimize-sibling-calls foo.c
> gdb ./foo
<gdb stuff>
(gdb) break f3
Breakpoint 1 at 0x4005b0: file /home/iant/foo.c, line 33.
(gdb) r
Starting program: /home/iant/gcc/gccgo4-objdir/foo 

Breakpoint 1, f3 (v2=1, v=0, p=<optimized out>) at /home/iant/foo.c:33
33        quit ("f3");
(gdb) where
#0  f3 (v2=1, v=0, p=<optimized out>) at foo.c:33
#1  0x00000000004005ea in f2 (v=<optimized out>, m=<optimized out>)
    at foo.c:56
#2  f2 (m=<optimized out>, v=<optimized out>) at foo.c:37
#3  0x0000000000400629 in f1 (m=m@entry=0x7fffffffe308) at foo.c:78
#4  0x0000000000400497 in main () at foo.c:87


Note how in the stack backtrace f2 appears twice although it does not call
itself.  This appears to be happening because pass_split_functions splits f2
into two functions, f2 and f2.part.0.  Then the ipa-inline pass inlines the
split function back into its single caller.  We are left with a single function
that has debug info showing an inlined call of itself.  gdb prints the
resulting stack trace showing the inlined call, but this doesn't make sense
since there is only one function and one stack frame here.  There is no user
visible inlining.


This happens with GCC 10 (current trunk) but does not happen with GCC 9.

(This causes trouble with the original Go code, because Go expects to be able
to reliably unwind the stack.  In this case there appears to be no marker
letting us know that the inlined call is to a split function that should be
ignored, so Go sees f2 calling itself, leading to incorrect stack unwinding.)


More information about the Gcc-bugs mailing list