Bug 91663 - split function can be re-inlined, leaving bad stack trace
Summary: split function can be re-inlined, leaving bad stack trace
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-09-05 01:00 UTC by Ian Lance Taylor
Modified: 2020-01-30 03:45 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-01-29 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ian Lance Taylor 2019-09-05 01:00:58 UTC
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.)
Comment 1 ian@gcc.gnu.org 2019-09-05 04:13:02 UTC
Author: ian
Date: Thu Sep  5 04:12:30 2019
New Revision: 275396

URL: https://gcc.gnu.org/viewcvs?rev=275396&root=gcc&view=rev
Log:
	PR tree-optimization/91663
	* go-lang.c (go_langhook_post_options): Clear
	flag_partial_inlining.

Modified:
    trunk/gcc/go/ChangeLog
    trunk/gcc/go/go-lang.c
Comment 2 Martin Liška 2020-01-29 11:39:34 UTC
@Ian: Can we close this?
Comment 3 Ian Lance Taylor 2020-01-30 03:45:28 UTC
I still see the problem with near-tip GCC.  Is there any reason to think that this problem is fixed?

(Note that I'm reporting what I consider to be a bug in the C compiler.  The patch attached to this PR is the Go frontend working around the problem in the middle-end.  It's not a fix for the bug.)