/* { dg-do run } */
/* { dg-options "-O2" } */

typedef __SIZE_TYPE__ size_t;
#define abort __builtin_abort

void *
__attribute__ ((alloc_size (1)))
__attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((noinline))
alloc_func_long (long sz)
{
  return __builtin_malloc (sz);
}

void *
__attribute__ ((alloc_size (1)))
__attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((noinline))
alloc_func (size_t sz)
{
  return __builtin_malloc (sz);
}

void *
__attribute__ ((alloc_size (1, 2)))
__attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((noinline))
calloc_func (size_t cnt, size_t sz)
{
  return __builtin_calloc (cnt, sz);
}

void *
__attribute__ ((noinline))
unknown_allocator (size_t cnt, size_t sz)
{
  return __builtin_calloc (cnt, sz);
}

size_t
__attribute__ ((noinline))
test_unknown (size_t cnt, size_t sz)
{
  void *ch = unknown_allocator (cnt, sz);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

/* Malloc-like allocator.  */

size_t
__attribute__ ((noinline))
test_malloc (size_t sz)
{
  void *ch = alloc_func (sz);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc (size_t sz)
{
  void *ch = __builtin_malloc (sz);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc_cond (int cond)
{
  void *ch = __builtin_malloc (cond ? 32 : 64);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc_condphi (int cond)
{
  void *ch;
 
  if (cond)
    ch = __builtin_malloc (32);
  else
    ch = __builtin_malloc (64);

  size_t ret = __builtin_dynamic_object_size (ch, 0);

  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc_condphi2 (int cond, size_t in)
{
  void *ch;

  if (cond)
    ch = __builtin_malloc (in);
  else
    ch = __builtin_malloc (64);

  size_t ret = __builtin_dynamic_object_size (ch, 0);

  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
{
  void *ch;

  if (cond)
    ch = __builtin_malloc (in);
  else
    ch = __builtin_malloc (in2);

  size_t ret = __builtin_dynamic_object_size (ch, 0);

  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc_condphi4 (size_t sz, int cond)
{
  char *a = __builtin_malloc (sz);
  char b[sz / 2];

  size_t ret = __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
  __builtin_free (a);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
{
  char *a = __builtin_malloc (sz);

  size_t ret = __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
  __builtin_free (a);
  return ret;
}

long
__attribute__ ((noinline))
test_builtin_malloc_long (long sz, long off)
{
  char *a = alloc_func_long (sz);
  char *dest = a + off;
  long ret = __builtin_dynamic_object_size (dest, 0);
  return ret;
}

/* Calloc-like allocator.  */

size_t
__attribute__ ((noinline))
test_calloc (size_t cnt, size_t sz)
{
  void *ch = calloc_func (cnt, sz);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_calloc (size_t cnt, size_t sz)
{
  void *ch = __builtin_calloc (cnt, sz);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_calloc_cond (int cond1, int cond2)
{
  void *ch = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
  size_t ret = __builtin_dynamic_object_size (ch, 0);
  __builtin_free (ch);
  return ret;
}

size_t
__attribute__ ((noinline))
test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
{
  struct
    {
      int a;
      char b;
    } bin[cnt];

  char *ch = __builtin_calloc (cnt, sz);
  size_t ret = __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);

  __builtin_free (ch);
  return ret;
}

/* Passthrough functions.  */

size_t
__attribute__ ((noinline))
test_passthrough (size_t sz, char *in)
{
  char *bin = __builtin_malloc (sz);
  char *dest = __builtin_memcpy (bin, in, sz);

  size_t ret = __builtin_dynamic_object_size (dest, 0);
  __builtin_free (bin);
  return ret;
}

size_t
__attribute__ ((noinline))
test_passthrough_nonssa (char *in)
{
  char bin[__builtin_strlen (in) + 1];
  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);

  return __builtin_dynamic_object_size (dest, 0);
}

/* Variable length arrays.  */
size_t
__attribute__ ((noinline))
test_dynarray (size_t sz)
{
  char bin[sz];

  return __builtin_dynamic_object_size (bin, 0);
}

size_t
__attribute__ ((noinline))
test_dynarray_cond (int cond)
{
  char bin[cond ? 8 : 16];

  return __builtin_dynamic_object_size (bin, 0);
}

size_t
__attribute__ ((noinline))
test_deploop (size_t sz, size_t cond)
{
  char *bin = __builtin_alloca (32);

  for (size_t i = 0; i < sz; i++)
    if (i == cond)
      bin = __builtin_alloca (sz);

  return __builtin_dynamic_object_size (bin, 0);
}

/* Address expressions.  */

struct dynarray_struct
{
  long a;
  char c[16];
  int b;
};

size_t
__attribute__ ((noinline))
test_dynarray_struct (size_t sz, size_t off)
{
  struct dynarray_struct bin[sz];

  return __builtin_dynamic_object_size (&bin[off].c, 0);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_subobj (size_t sz, size_t off)
{
  struct dynarray_struct bin[sz];

  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
{
  struct dynarray_struct2
    {
      long a;
      int b;
      char c[sz];
    };

  struct dynarray_struct2 bin;

  *objsz = sizeof (bin);

  return __builtin_dynamic_object_size (&bin.c[off], 1);
}

/* See pr #108522.  */

#define DEFSTRUCT(_s, _n) \
  struct DS								      \
    {									      \
      char a[_n];							      \
      unsigned long long b;						      \
      int c;								      \
      char d[2 * _n];							      \
    } _s

size_t
__attribute__ ((noinline))
test_dynarray_struct_member_b (size_t sz)
{
  DEFSTRUCT (s, sz);

  return __builtin_dynamic_object_size (&s.b, 0);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_member_c (size_t sz)
{
  DEFSTRUCT (s, sz);

  return __builtin_dynamic_object_size (&s.c, 0);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_member_d (size_t sz, size_t offset)
{
  DEFSTRUCT (s, sz);

  return __builtin_dynamic_object_size (&s.d[offset], 0);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_member_subobj_b (size_t sz)
{
  DEFSTRUCT (s, sz);

  return __builtin_dynamic_object_size (&s.b, 1);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_member_subobj_c (size_t sz)
{
  DEFSTRUCT (s, sz);

  return __builtin_dynamic_object_size (&s.c, 1);
}

size_t
__attribute__ ((noinline))
test_dynarray_struct_member_subobj_d (size_t sz, size_t offset)
{
  DEFSTRUCT (s, sz);

  return __builtin_dynamic_object_size (&s.d[offset], 1);
}

size_t
__attribute__ ((noinline))
test_substring (size_t sz, size_t off)
{
  char str[sz];

  return __builtin_dynamic_object_size (&str[off], 0);
}

struct S2
{
  char arr[7];
};

struct S1
{
  int pad;
  struct S2 s2;
};

static long
g (struct S1 *s1)
{
  struct S2 *s2 = &s1->s2;
  return __builtin_dynamic_object_size (s2->arr, 0);
}

long
__attribute__ ((noinline))
test_alloc_nested_structs (int x)
{
  struct S1 *s1 = __builtin_malloc (x);
  return g (s1);
}

/* POINTER_PLUS expressions.  */

size_t
__attribute__ ((noinline))
test_substring_ptrplus (size_t sz, size_t off)
{
  int str[sz];

  return __builtin_dynamic_object_size (str + off, 0);
}

size_t
__attribute__ ((noinline))
test_substring_ptrplus2 (size_t sz, size_t off, size_t off2)
{
  int str[sz];
  int *ptr = &str[off];

  return __builtin_dynamic_object_size (ptr + off2, 0);
}

/* Function parameters.  */

size_t
__attribute__ ((access (__read_write__, 1, 2)))
__attribute__ ((noinline))
test_parmsz_simple (void *obj, size_t sz)
{
  return __builtin_dynamic_object_size (obj, 0);
}

size_t
__attribute__ ((access (__read_write__, 2, 1)))
__attribute__ ((noinline))
test_parmsz_simple2 (size_t sz, char obj[])
{
  return __builtin_dynamic_object_size (obj, 0);
}

/* Implicitly constructed access attributes not supported yet.  */
size_t
__attribute__ ((noinline))
test_parmsz_simple3 (size_t sz, char obj[sz])
{
  return __builtin_dynamic_object_size (obj, 0);
}

size_t
__attribute__ ((noinline))
__attribute__ ((access (__read_write__, 1, 2)))
test_parmsz (void *obj, size_t sz, size_t off)
{
  return __builtin_dynamic_object_size (obj + off, 0);
}

size_t
__attribute__ ((access (__read_write__, 1, 2)))
__attribute__ ((noinline))
test_parmsz_scaled (int *obj, size_t sz)
{
  return __builtin_dynamic_object_size (obj, 0);
}

size_t
__attribute__ ((noinline))
__attribute__ ((access (__read_write__, 1, 2)))
test_parmsz_scaled_off (int *obj, size_t sz, size_t off)
{
  return __builtin_dynamic_object_size (obj + off, 0);
}

size_t
__attribute__ ((access (__read_write__, 1, 3)))
__attribute__ ((noinline))
test_parmsz_unknown (void *obj, void *unknown, size_t sz, int cond)
{
  return __builtin_dynamic_object_size (cond ? obj : unknown, 0);
}

struct S;
size_t
__attribute__ ((access (__read_write__, 1, 2)))
__attribute__ ((noinline))
test_parmsz_extern (struct S *obj, size_t sz)
{
  return __builtin_dynamic_object_size (obj, 0);
}

/* Implicitly constructed access attributes not supported yet.  */
size_t
__attribute__ ((noinline))
test_parmsz_internal (size_t sz, double obj[][sz])
{
  return __builtin_dynamic_object_size (obj, 0);
}

size_t
__attribute__ ((access (__read_write__, 2, 1)))
__attribute__ ((noinline))
test_parmsz_internal2 (size_t sz, double obj[][sz])
{
  return __builtin_dynamic_object_size (obj, 0);
}

size_t
__attribute__ ((noinline))
test_parmsz_internal3 (size_t sz1, size_t sz2, double obj[sz1][sz2])
{
  return __builtin_dynamic_object_size (obj, 0);
}

/* Loops.  */

size_t
__attribute__ ((noinline))
__attribute__ ((access (__read_write__, 1, 2)))
test_loop (int *obj, size_t sz, size_t start, size_t end, int incr)
{
  int *ptr = obj + start;

  for (int i = start; i != end; i = i + incr)
    {
      ptr = ptr + incr;
      if (__builtin_dynamic_object_size (ptr, 0) == 0)
	return 0;
    }

  return __builtin_dynamic_object_size (ptr, 0);
}

/* Other tests.  */

struct TV4
{
  __attribute__((vector_size (sizeof (int) * 4))) int v;
};

struct TV4 val3;
int *
test_pr105736 (struct TV4 *a)
{
  return &a->v[0];
}

unsigned nfails = 0;

#define FAIL() ({ \
  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
  nfails++;								      \
})

int
main (int argc, char **argv)
{
  size_t outsz = test_unknown (32, 42);
  if (outsz != -1 && outsz != 32)
    FAIL ();
  if (test_malloc (2048) != 2048)
    FAIL ();
  if (test_builtin_malloc (2048) != 2048)
    FAIL ();
  if (test_builtin_malloc_cond (1) != 32)
    FAIL ();
  if (test_builtin_malloc_cond (0) != 64)
    FAIL ();
  if (test_builtin_malloc_condphi (1) != 32)
    FAIL ();
  if (test_builtin_malloc_condphi (0) != 64)
    FAIL ();
  if (test_builtin_malloc_condphi2 (1, 128) != 128)
    FAIL ();
  if (test_builtin_malloc_condphi2 (0, 128) != 64)
    FAIL ();
  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
    FAIL ();
  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
    FAIL ();
  if (test_builtin_malloc_condphi4 (128, 1) != 64)
    FAIL ();
  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
    FAIL ();
  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
    FAIL ();
  long x = 42;
  if (test_builtin_malloc_long (x, 0) != x)
    FAIL ();
  if (test_calloc (2048, 4) != 2048 * 4)
    FAIL ();
  if (test_builtin_calloc (2048, 8) != 2048 * 8)
    FAIL ();
  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
    FAIL ();
  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
    FAIL ();
  if (test_builtin_calloc_condphi (128, 1, 0)
      != 128 * sizeof (struct { int a; char b; }))
    FAIL ();
  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
    FAIL ();
  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
      != __builtin_strlen (argv[0]) + 1)
    FAIL ();
  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
    FAIL ();
  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
    FAIL ();
  if (test_dynarray_struct (42, 4) !=
      ((42 - 4) * sizeof (struct dynarray_struct)
       - __builtin_offsetof (struct dynarray_struct, c)))
    FAIL ();
  if (test_dynarray_struct (42, 48) != 0)
    FAIL ();
  if (test_substring (128, 4) != 128 - 4)
    FAIL ();
  if (test_substring (128, 142) != 0)
    FAIL ();
  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
    FAIL ();
  if (test_dynarray_struct_subobj (42, 48) != 0)
    FAIL ();
  size_t objsz = 0;
  if (test_dynarray_struct_subobj2 (42, 4, &objsz)
    != objsz - 4 - sizeof (long) - sizeof (int))
    FAIL ();
  DEFSTRUCT(ds, 64);
  const size_t n = sizeof (ds.a);
  if (test_dynarray_struct_member_b (n)
      != sizeof (ds) - __builtin_offsetof (struct DS, b))
    FAIL ();
  if (test_dynarray_struct_member_c (n)
      != sizeof (ds) - __builtin_offsetof (struct DS, c))
    FAIL ();
  if (test_dynarray_struct_member_d (n, 0)
      != sizeof (ds) - __builtin_offsetof (struct DS, d))
    FAIL ();
  if (test_dynarray_struct_member_subobj_b (n) != sizeof (ds.b))
    FAIL ();
  if (test_dynarray_struct_member_subobj_c (n) != sizeof (ds.c))
    FAIL ();
  if (test_dynarray_struct_member_subobj_d (n, n - 2)
      != sizeof (ds) - __builtin_offsetof (struct DS, d) - n + 2)
    FAIL ();
  if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int))
    FAIL ();
  if (test_substring_ptrplus (128, 142) != 0)
    FAIL ();
  if (test_substring_ptrplus2 (128, 4, 4) != (128 - 8) * sizeof (int))
    FAIL ();
  if (test_substring_ptrplus2 (128, 4, -3) != (128 - 1) * sizeof (int))
    FAIL ();
  if (test_dynarray_cond (0) != 16)
    FAIL ();
  if (test_dynarray_cond (1) != 8)
    FAIL ();
  if (test_alloc_nested_structs (42) != 42 - sizeof (int))
    FAIL ();
  if (test_deploop (128, 4) != 128)
    FAIL ();
  if (test_deploop (128, 129) != 32)
    FAIL ();
  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
      != __builtin_strlen (argv[0]) + 1)
    FAIL ();
  if (test_parmsz_simple2 (__builtin_strlen (argv[0]) + 1, argv[0])
      != __builtin_strlen (argv[0]) + 1)
    FAIL ();
  /* Only explicitly added access attributes are supported for now.  */
  if (test_parmsz_simple3 (__builtin_strlen (argv[0]) + 1, argv[0]) != -1)
    FAIL ();
  int arr[42];
  if (test_parmsz_scaled (arr, 42) != sizeof (arr))
    FAIL ();
  if (test_parmsz_scaled (arr, 40) != 40 * sizeof (int))
    FAIL ();
  /* __bdos cannot see the actual size of ARR, so it will return what it was
     passed.  Fortunately though the overflow warnings see this caller side and
     warns of the problematic size.  */
  if (test_parmsz_scaled (arr, 44) != 44 * sizeof (int)) /* { dg-warning "-Wstringop-overflow=" } */
    FAIL ();
  if (test_parmsz_unknown (argv[0], argv[0], __builtin_strlen (argv[0]) + 1, 0)
      != -1)
  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, -1) != 0)
    FAIL ();
  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, 0)
      != __builtin_strlen (argv[0]) + 1)
    FAIL ();
  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
		   __builtin_strlen (argv[0])) != 1)
    FAIL ();
  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
		   __builtin_strlen (argv[0]) + 2) != 0)
    FAIL ();
  if (test_parmsz_scaled_off (arr, 42, 2) != 40 * sizeof (int))
    FAIL ();
  struct S *s;
  if (test_parmsz_extern (s, 42) != -1)
    FAIL ();
  double obj[4][4];
  if (test_parmsz_internal (4, obj) != -1)
    FAIL ();
  if (test_parmsz_internal2 (4, obj) != -1)
    FAIL ();
  if (test_parmsz_internal3 (4, 4, obj) != -1)
    FAIL ();
  if (test_loop (arr, 42, 0, 32, 1) != 10 * sizeof (int))
    FAIL ();
  if (test_loop (arr, 42, 32, -1, -1) != 0)
    FAIL ();
  if (test_loop (arr, 42, 32, 10, -1) != 32 * sizeof (int))
    FAIL ();
  if (test_loop (arr, 42, 42, 0, -1) != 42 * sizeof (int))
    FAIL ();
  if (test_loop (arr, 42, 44, 0, -1) != 0)
    FAIL ();
  if (test_loop (arr, 42, 20, 52, 1) != 0)
    FAIL ();
  /* pr105736.  */
  int *t = test_pr105736 (&val3);
  if (__builtin_dynamic_object_size (t, 0) != -1)
    FAIL ();

  if (nfails > 0)
    __builtin_abort ();

  return 0;
}
