/* PR middle-end/37275 */
/* { dg-do compile { target ia32 } } */
/* { dg-options "-g -dA -O2 -march=i686 -fstack-protector" } */
/* { dg-require-visibility "" } */

typedef __SIZE_TYPE__ size_t;
extern void *memcpy (void *, const void *, size_t);
extern void *malloc (size_t);

typedef int A;

struct B
{
  int x;
};

struct C
{
  struct F *c1;
  void *c2;
};

enum D
{
  D0,
  D1
};

struct E
{
  struct E *e1;
  struct E *e2;
  struct B e3;
  void (*fn) (void *);
  void *fn_data;
  enum D e4;
  _Bool e5;
  _Bool e6;
};

struct F
{
  unsigned f1;
  A f2;
  int f3;
};

struct G
{
  void (*fn) (void *data);
  void *data;
  struct C g1;
  struct E *t;
};

extern void fn1 (A * m);
static inline void
fn2 (A *x)
{
  if (!__sync_bool_compare_and_swap (x, 0, 1))
    fn1 (x);
}

extern __thread struct G thr __attribute__ ((visibility ("hidden")));
static inline struct G *
fn3 (void)
{
  return &thr;
}

extern struct B *fn4 (void);
extern struct B a;

static inline struct B *
fn5 (_Bool x)
{
  struct E *t = fn3 ()->t;
  if (t)
    return &t->e3;
  else if (x)
    return fn4 ();
  else
    return &a;
}

void
fn6 (struct E *t, struct E *e1_t,
		struct B *prev_e3)
{
  t->e1 = e1_t;
  t->e3 = *prev_e3;
  t->e4 = D0;
  t->e5 = 0;
  t->e6 = 0;
  t->e2 = ((void *) 0);
}

void
test (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *), long x, long y, _Bool z)
{
  struct G *thr = fn3 ();
  struct F *c1 = thr->g1.c1;
  if (!z || c1 == 0 || (unsigned) c1->f3 > 64 * c1->f1)
    {
      struct E t;

      fn6 (&t, thr->t, fn5 (0));
      if (thr->t)
	t.e6 = thr->t->e6;
      thr->t = &t;
      if (__builtin_expect (cpyfn != ((void *) 0), 0))
	{
	  char buf[x + y - 1];
	  char *arg = (char *) (((unsigned long) buf + y - 1)
				& ~(unsigned long) (y - 1));
	  cpyfn (arg, data);
	  fn (arg);
	}
    }
  else
    {
      struct E *t;
      struct E *e1 = thr->t;
      char *arg;

      t = malloc (sizeof (*t) + x + y - 1);
      arg = (char *) (((unsigned long) (t + 1) + y - 1)
		      & ~(unsigned long) (y - 1));
      fn6 (t, e1, fn5 (0));
      thr->t = t;
      if (cpyfn)
	cpyfn (arg, data);
      else
	memcpy (arg, data, x);
      thr->t = e1;
      fn2 (&c1->f2);
    }
}
