#include <string.h>
#include "analyzer-decls.h"

/* Function for thwarting expansion of memcpy by optimizer.  */

typedef void * (*memcpy_t) (void *dst, const void *src, size_t n);
  
static memcpy_t __attribute__((noinline))
get_memcpy (void)
{
  return memcpy;
}

void *test_1 (void *dst, void *src, size_t n)
{
  void *result = memcpy (dst, src, n);
  __analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
  return result;
}

void *test_1a (void *dst, void *src, size_t n)
{
  void *result = __memcpy_chk (dst, src, n, -1);
  __analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
  return result;
}

void *test_1b (void *dst, void *src, size_t n)
{
  memcpy_t fn = get_memcpy ();
  void *result = fn (dst, src, n);
  __analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
  return result;
}

void test_2 (int i)
{
  int j;
  memcpy (&j, &i, sizeof (int));
  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
}

void test_2a (int i)
{
  int j;
  __memcpy_chk (&j, &i, sizeof (int), sizeof (int));
  __analyzer_eval (i == j);  /* { dg-warning "TRUE" } */
}

void test_2b (int i)
{
  int j;
  memcpy_t fn = get_memcpy ();
  fn (&j, &i, sizeof (int));
  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
}

void test_3 (void *src, size_t n)
{
  char buf[40], other[40];
  buf[0] = 'a';
  other[0] = 'b';
  __analyzer_eval (buf[0] == 'a');    /* { dg-warning "TRUE" } */
  __analyzer_eval (other[0] == 'b');  /* { dg-warning "TRUE" } */

  memcpy (buf, src, n);
  __analyzer_eval (buf[0] == 'a');    /* { dg-warning "UNKNOWN" } */
  __analyzer_eval (other[0] == 'b');  /* { dg-warning "TRUE" } */
}

void test_3b (void *src, size_t n)
{
  char buf[40], other[40];
  memcpy_t fn = get_memcpy ();
  buf[0] = 'a';
  other[0] = 'b';
  __analyzer_eval (buf[0] == 'a');    /* { dg-warning "TRUE" } */
  __analyzer_eval (other[0] == 'b');  /* { dg-warning "TRUE" } */

  fn (buf, src, n);
  __analyzer_eval (buf[0] == 'a');    /* { dg-warning "UNKNOWN" } */
  __analyzer_eval (other[0] == 'b');  /* { dg-warning "TRUE" } */
}

/* Overwriting a zeroed buffer, then memcpy of the result.  */

void test_4 (int a, int b)
{
  int src[1024];
  int dst[1024];
  memset (src, 0, sizeof (src));
  src[42] = a;
  src[100] = b;
  __analyzer_eval (src[0] == 0);    /* { dg-warning "TRUE" } */
  __analyzer_eval (src[42] == a);    /* { dg-warning "TRUE" } */
  __analyzer_eval (src[100] == b);    /* { dg-warning "TRUE" } */
  __analyzer_eval (src[1023] == 0);    /* { dg-warning "TRUE" } */

  memcpy (dst, src, sizeof (src));
  __analyzer_eval (dst[0] == 0);    /* { dg-warning "TRUE" } */
  __analyzer_eval (dst[42] == a);    /* { dg-warning "TRUE" } */
  __analyzer_eval (dst[100] == b);    /* { dg-warning "TRUE" } */
  __analyzer_eval (dst[1023] == 0);    /* { dg-warning "TRUE" } */  
}

void test_4b (int a, int b)
{
  int src[1024];
  int dst[1024];
  memcpy_t fn = get_memcpy ();
  memset (src, 0, sizeof (src));
  src[42] = a;
  src[100] = b;
  __analyzer_eval (src[0] == 0);    /* { dg-warning "TRUE" } */
  __analyzer_eval (src[42] == a);    /* { dg-warning "TRUE" } */
  __analyzer_eval (src[100] == b);    /* { dg-warning "TRUE" } */
  __analyzer_eval (src[1023] == 0);    /* { dg-warning "TRUE" } */

  fn (dst, src, sizeof (src));
  __analyzer_eval (dst[0] == 0);    /* { dg-warning "TRUE" } */
  __analyzer_eval (dst[42] == a);    /* { dg-warning "TRUE" } */
  __analyzer_eval (dst[100] == b);    /* { dg-warning "TRUE" } */
  __analyzer_eval (dst[1023] == 0);    /* { dg-warning "TRUE" } */  
}

/* Populating a buffer from an unknown buffer.  */

void test_5 (void *src, size_t sz)
{
  char dst[1024];
  memcpy (dst, src, sizeof (dst));
  __analyzer_eval (dst[0] == 0); /* { dg-warning "UNKNOWN" } */
  __analyzer_eval (dst[1023] == 0); /* { dg-warning "UNKNOWN" } */
}

void test_5b (void *src, size_t sz)
{
  char dst[1024];
  memcpy_t fn = get_memcpy ();
  fn (dst, src, sizeof (dst));
  __analyzer_eval (dst[0] == 0); /* { dg-warning "UNKNOWN" } */
  __analyzer_eval (dst[1023] == 0); /* { dg-warning "UNKNOWN" } */
}

/* Zero-sized memcpy.  */

void test_6 (void *dst, void *src)
{
  memcpy (dst, src, 0);
}

void test_6b (void *dst, void *src)
{
  memcpy_t fn = get_memcpy ();
  fn (dst, src, 0);
}

/* memcpy to string literal.  */

void test_7 (void *src, size_t sz)
{
  memcpy ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
}

void test_7b (void *src, size_t sz)
{
  memcpy ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
}
