/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
   { dg-do compile }
   { dg-options "-O2 -Wall" }
   { dg-require-effective-target alloca } */

#define ATTR(...) __attribute__ ((__VA_ARGS__))

struct A { int a, b, c; };
struct B { int a, b, c[]; };

extern int g1[5], g2[5], g3[5], g4[5], g5[5];

void sink (void*, ...);

ATTR (noipa) void*
return_2_locals (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  void *p = b;
  if (i < 0)
    p = a;

  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_2_locals_after_2_globals (int i, int j)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */

  int *p;
  if (i < 0)
    p = g1;
  else
    p = g2;

  sink (p);

  if (j < 0)
    p = a;
  else
    p = b;

  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_3_locals (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */

  void *p = b + 1;
  if (i < 0)
    p = a;
  else if (0 < i)
    p = c + 2;

  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_5_locals (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */
  int d[4];         /* { dg-message "declared here" } */
  int e[5];         /* { dg-message "declared here" } */

  void *p = &c[2];
  if (i < -1)
    p = a;
  else if (i < 0)
    p = &b[1];
  else if (1 < i)
    p = &e[4];
  else if (0 < i)
    p = &d[3];

  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_5_locals_switch (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */
  int d[4];         /* { dg-message "declared here" } */
  int e[5];         /* { dg-message "declared here" } */

  void *p = 0;

  switch (i)
    {
    case 0: p = &a[1]; break;
    case 1: p = &b[2]; break;
    case 2: p = &c[3]; break;
    case 3: p = &d[4]; break;
    default: p = &e[5]; break;
    }

  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_1_global_4_locals (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */
  int d[4];         /* { dg-message "declared here" } */

  void *p = c;
  if (i < -1)
    sink (p = a);
  else if (i < 0)
    sink (p = b);
  else if (1 < i)
    sink (p = g1);
  else if (0 < i)
    sink (p = d);

  sink (p, a, b, c, d);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_1_global_4_locals_switch (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */
  int d[4];         /* { dg-message "declared here" } */

  void *p = 0;

  switch (i)
    {
    case 0: p = &a[0]; break;
    case 1: p = &b[1]; break;
    case 2: p = &c[2]; break;
    case 3: p = &d[3]; break;
    }

  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_2_globals_3_locals (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */

  void *p = c;
  if (i < -1)
    p = a;
  else if (i < 0)
    p = b;
  else if (1 < i)
    p = g1;
  else if (0 < i)
    p = g2;

  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_3_globals_2_locals (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */

  void *p = g3;
  if (i < -1)
    p = a;
  else if (i < 0)
    p = b;
  else if (1 < i)
    p = g1;
  else if (0 < i)
    p = g2;

  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_4_globals_1_local (int i)
{
  int a[1];         /* { dg-message "declared here" } */

  void *p = g3;
  if (i < -1)
    p = a;
  else if (i < 0)
    p = g1;
  else if (1 < i)
    p = g2;
  else if (0 < i)
    p = g4;

  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_all_globals (int i)
{
  void *p = g4;
  if (i < -1)
    p = g1;
  else if (i < 0)
    p = g2;
  else if (1 < i)
    p = g3;
  else if (0 < i)
    p = g5;
  return p;
}


ATTR (noipa) void*
return_2_alloca_local_cstoff (int n, int i)
{
  int *a = __builtin_alloca (n);  /* { dg-message "declared here" } */
  int *b = __builtin_alloca (n);  /* { dg-message "declared here" } */
  int *p = i < 0 ? a : b;

  p += 1;
  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_alloca_local_cstoff (int n, int i)
{
  int *a = __builtin_alloca (n);  /* { dg-message "declared here" } */
  int b[2];                       /* { dg-message "declared here" } */

  int *p = b;
  if (i < 0)
    p = a;

  p += 1;
  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_local_alloca_cstoff (int n, int i)
{
  int a[2];                       /* { dg-message "declared here" } */
  int *b = __builtin_alloca (n);  /* { dg-message "declared here" } */
  int *p = b;
  if (i < 0)
    p = a;

  p += 1;
  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_2_locals_cstoff (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */

  int *p = b;
  if (i < 0)
    p = a;

  p += 1;
  sink (p);

  return p;         /* { dg-warning "function returns address of local" } */
}

ATTR (noipa) void*
return_2_globals_3_locals_cstoff (int i)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */
  int c[3];         /* { dg-message "declared here" } */

  int *p = c;
  if (i < -1)
    p = a;
  else if (i < 0)
    p = b;
  else if (1 < i)
    p = g1;
  else if (0 < i)
    p = g2;

  p += 1;
  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_3_globals_alloca_local_varoff (int n, int i, int j)
{
  int *a = __builtin_alloca (n);  /* { dg-message "declared here" } */
  int b[2];                       /* { dg-message "declared here" } */

  int *p = g3;
  if (i < -1)
    p = a;
  else if (i < 0)
    p = b;
  else if (1 < i)
    p = g1;
  else if (0 < i)
    p = g2;

  p += j;
  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

ATTR (noipa) void*
return_3_globals_2_locals_varoff (int i, int j)
{
  int a[1];         /* { dg-message "declared here" } */
  int b[2];         /* { dg-message "declared here" } */

  int *p = g3;
  if (i < -1)
    p = a;
  else if (i < 0)
    p = b;
  else if (1 < i)
    p = g1;
  else if (0 < i)
    p = g2;

  p += j;
  sink (p);

  return p;         /* { dg-warning "function may return address of local" } */
}

