http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59099

            Bug ID: 59099
           Summary: Erroneous register allocation on 32-bit x86 using
                    regparm
           Product: gcc
           Version: 4.8.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ian at airs dot com
                CC: vmakarov at redhat dot com

The appended test case should run without error and return 0.  When I compile
it with  -O2 -fPIC -m32 for 32-bit x86, it crashes at runtime with a
segmentation violation.

The generated code is incorrect.  The loop is entered like this:

    movl    12(%esp), %eax
    leal    4(%esi), %ecx
    movl    %edi, 64(%esp)
    xorl    %esi, %esi
    subl    $1, %eax
    movl    %eax, 20(%esp)
    jmp    .L7


Note that %eax holds a value stored in 20(%esp), which as we will see is used
to terminate the loop.  At .L7 we see this:

.L7:
    movl    4(%esp), %edi
    testl    %edi, %edi
    jne    .L5
    cmpl    %edx, 20(%esp)
    jne    .L5

Note that nothing changes %eax.  Note the comparison with 20(%esp).

At .L5 we see this:

.L5:
    movl    64(%esp), %edi
    movl    (%eax), %eax

Note that we are loading a value from %eax.  But at this point it still holds
the value it was given before the loop, which is a counter, not a pointer.

I have not tried to track down the problem, but superficially it appears that
the register allocator has decided to rematerialize the pointer p from the
register parameter, without realizing that the register has changed since the
start of the function.

This is reduced from code in the libgo library.  The libgo code does not use
regparm, but the function in question is a static function and the optimizers
assign it regparm ((1)).

void (*pfn)(void);

struct s
{
  void** q;
  int h;
  int t;
  int s;
};


void* f (struct s *, struct s *) __attribute__ ((noinline, regparm(1)));

void*
__attribute__ ((regparm(1)))
f (struct s *p, struct s *p2)
{
  void *gp, *gp1;
  int t, h, s, t2, h2, c, i;

  if (p2->h == p2->t)
    return 0;

  (*pfn) ();

  h = p->h;
  t = p->t;
  s = p->s;

  h2 = p2->h;
  t2 = p2->t;

  gp = p2->q[h2++];

  c = (t2 - h2) / 2;
  for (i = 0; i != c; i++)
    {
      if (t == h || (h == 0 && t == s - 1))
    break;
      gp1 = p2->q[h2++];
      p->q[t++] = gp1;
      if (t == s)
    t = 0;
    }

  p2->h = h2;
  return gp;
}

static void fn () { }

int
main()
{
  struct s s1, s2;
  void *q[10];

  pfn = fn;

  s1.q = q;
  s1.h = 0;
  s1.t = 2;
  s1.s = 4;

  s2.q = q;
  s2.h = 0;
  s2.t = 4;
  s2.s = 2;

  f (&s1, &s2);

  return 0;
}

Reply via email to