/* The z13 stpcpy implementation plays some alignment tricks for good
   performance.  This test tries to make sure it works correctly and
   does not access bytes beyond the source and destination
   strings.  */

/* { dg-do run } */
/* { dg-require-effective-target s390_vx } */
/* { dg-options "-O3 -mzarch -march=z13" } */

#include <stdio.h>
#include <sys/mman.h>

#define PAGE_SIZE 4096

struct {
  char unused[PAGE_SIZE - 32];
  char m32[15]; /* page bndry - 32 */
  char m17[1];
  char m16[1];
  char m15[14];
  char m1[1];
  char next_page[PAGE_SIZE];
} s, d __attribute__((aligned(PAGE_SIZE)));

char *__attribute__((noinline))
my_stpcpy(char *dest, const char *src)
{
  return __builtin_stpcpy (dest, src);
}

void __attribute__ ((noinline))
check (char *dest, char *src, size_t len)
{
  char *result;

  result = my_stpcpy (dest, src);
  if (result != dest + len)
    __builtin_abort ();
  if (__builtin_memcmp (src, dest, len) != 0)
    __builtin_abort ();
}

int
main ()
{
  char *src[5] = { s.m32, s.m17, s.m16, s.m15, s.m1 };
  char *dst[5] = { d.m32, d.m17, d.m16, d.m15, d.m1 };
  int len[8] = { 33, 32, 31, 17, 16, 15, 1, 0 };
  int i, j, k;
  char backup;

  for (i = 0; i < sizeof (s); i++)
    ((char*)&s)[i] = i % 26 + 97;

  for (i = 0; i < 5; i++)
    for (j = 0; j < 5; j++)
      for (k = 0; k < 8; k++)
	{
	  backup = src[j][len[k]];
	  src[j][len[k]] = 0;
	  __builtin_memset (&d, 0, sizeof (d));
	  check (dst[i], src[j], len[k]);
	  src[j][len[k]] = backup;
	}

  /* Make all source strings end before the page boundary.  */
  backup = s.m1[0];
  s.m1[0] = 0;

  if (mprotect (&s.next_page, PAGE_SIZE, PROT_NONE) == -1)
    perror ("mprotect src");

  for (i = 0; i < 5; i++)
    for (j = 0; j < 5; j++)
      check (dst[i], src[j],
	     PAGE_SIZE - ((unsigned long)src[j] & ((1UL << 12) - 1)) - 1);

  if (mprotect (&s.next_page, PAGE_SIZE, PROT_READ | PROT_WRITE) == -1)
    perror ("mprotect src");

  s.m1[0] = backup;

  if (mprotect (&d.next_page, PAGE_SIZE, PROT_NONE) == -1)
    perror ("mprotect dst");

  for (i = 0; i < 5; i++)
    for (j = 0; j < 5; j++)
      {
	int len = PAGE_SIZE - ((unsigned long)dst[i] & ((1UL << 12) - 1)) - 1;
	char backup = src[j][len];

	src[j][len] = 0;
	__builtin_memset (&d, 0,
			  (unsigned long)&d.next_page - (unsigned long)&d);
	check (dst[i], src[j], len);
	src[j][len] = backup;
      }

  return 0;
}
