/* This testcase is part of GDB, the GNU debugger.

   Copyright 2025-2026 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Exercise unwinding AArch64's SVE registers from a signal frame.   */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/prctl.h>
#include <unistd.h>

static int second_vl = 0;

static void
initialize_sve_state_main ()
{
  __asm __volatile ("dup z0.b, -1");
  __asm __volatile ("dup z1.b, -1");
  __asm __volatile ("dup z2.b, -1");
  __asm __volatile ("dup z3.b, -1");
  __asm __volatile ("dup z4.b, -1");
  __asm __volatile ("dup z5.b, -1");
  __asm __volatile ("dup z6.b, -1");
  __asm __volatile ("dup z7.b, -1");
  __asm __volatile ("dup z8.b, -1");
  __asm __volatile ("dup z9.b, -1");
  __asm __volatile ("dup z10.b, -1");
  __asm __volatile ("dup z11.b, -1");
  __asm __volatile ("dup z12.b, -1");
  __asm __volatile ("dup z13.b, -1");
  __asm __volatile ("dup z14.b, -1");
  __asm __volatile ("dup z15.b, -1");
  __asm __volatile ("dup z16.b, -1");
  __asm __volatile ("dup z17.b, -1");
  __asm __volatile ("dup z18.b, -1");
  __asm __volatile ("dup z19.b, -1");
  __asm __volatile ("dup z20.b, -1");
  __asm __volatile ("dup z21.b, -1");
  __asm __volatile ("dup z22.b, -1");
  __asm __volatile ("dup z23.b, -1");
  __asm __volatile ("dup z24.b, -1");
  __asm __volatile ("dup z25.b, -1");
  __asm __volatile ("dup z26.b, -1");
  __asm __volatile ("dup z27.b, -1");
  __asm __volatile ("dup z28.b, -1");
  __asm __volatile ("dup z29.b, -1");
  __asm __volatile ("dup z30.b, -1");
  __asm __volatile ("dup z31.b, -1");
  __asm __volatile ("ptrue p0.d");
  __asm __volatile ("ptrue p1.d");
  __asm __volatile ("ptrue p2.d");
  __asm __volatile ("ptrue p3.d");
  __asm __volatile ("ptrue p4.d");
  __asm __volatile ("ptrue p5.d");
  __asm __volatile ("ptrue p6.d");
  __asm __volatile ("ptrue p7.d");
  __asm __volatile ("ptrue p8.d");
  __asm __volatile ("ptrue p9.d");
  __asm __volatile ("ptrue p10.d");
  __asm __volatile ("ptrue p11.d");
  __asm __volatile ("ptrue p12.d");
  __asm __volatile ("ptrue p13.d");
  __asm __volatile ("ptrue p14.d");
  __asm __volatile ("ptrue p15.d");
  __asm __volatile ("setffr");
}

static void
initialize_sve_state_sighandler ()
{
  __asm __volatile ("dup z0.b, -2");
  __asm __volatile ("dup z1.b, -2");
  __asm __volatile ("dup z2.b, -2");
  __asm __volatile ("dup z3.b, -2");
  __asm __volatile ("dup z4.b, -2");
  __asm __volatile ("dup z5.b, -2");
  __asm __volatile ("dup z6.b, -2");
  __asm __volatile ("dup z7.b, -2");
  __asm __volatile ("dup z8.b, -2");
  __asm __volatile ("dup z9.b, -2");
  __asm __volatile ("dup z10.b, -2");
  __asm __volatile ("dup z11.b, -2");
  __asm __volatile ("dup z12.b, -2");
  __asm __volatile ("dup z13.b, -2");
  __asm __volatile ("dup z14.b, -2");
  __asm __volatile ("dup z15.b, -2");
  __asm __volatile ("dup z16.b, -2");
  __asm __volatile ("dup z17.b, -2");
  __asm __volatile ("dup z18.b, -2");
  __asm __volatile ("dup z19.b, -2");
  __asm __volatile ("dup z20.b, -2");
  __asm __volatile ("dup z21.b, -2");
  __asm __volatile ("dup z22.b, -2");
  __asm __volatile ("dup z23.b, -2");
  __asm __volatile ("dup z24.b, -2");
  __asm __volatile ("dup z25.b, -2");
  __asm __volatile ("dup z26.b, -2");
  __asm __volatile ("dup z27.b, -2");
  __asm __volatile ("dup z28.b, -2");
  __asm __volatile ("dup z29.b, -2");
  __asm __volatile ("dup z30.b, -2");
  __asm __volatile ("dup z31.b, -2");
  __asm __volatile ("pfalse p0.b");
  __asm __volatile ("pfalse p1.b");
  __asm __volatile ("pfalse p2.b");
  __asm __volatile ("pfalse p3.b");
  __asm __volatile ("pfalse p4.b");
  __asm __volatile ("pfalse p5.b");
  __asm __volatile ("pfalse p6.b");
  __asm __volatile ("pfalse p7.b");
  __asm __volatile ("pfalse p8.b");
  __asm __volatile ("pfalse p9.b");
  __asm __volatile ("pfalse p10.b");
  __asm __volatile ("pfalse p11.b");
  __asm __volatile ("pfalse p12.b");
  __asm __volatile ("pfalse p13.b");
  __asm __volatile ("pfalse p14.b");
  __asm __volatile ("pfalse p15.b");
  __asm __volatile ("setffr");
}

/* Set new value for the SVE vector length.
   Return the value that was set.  */

static int
set_vl (int vl)
{
  int rc;

  rc = prctl (PR_SVE_SET_VL, vl, 0, 0, 0);
  if (rc < 0)
    {
      perror ("FAILED to PR_SVE_SET_VL");
      exit (EXIT_FAILURE);
    }

  return rc & PR_SVE_VL_LEN_MASK;
}

static void
sighandler (int sig, siginfo_t *info, void *ucontext)
{
  /* Set vector length to the second value.  */
  second_vl = set_vl (second_vl);
  initialize_sve_state_sighandler ();
  printf ("sighandler: second_vl = %d\n", second_vl); /* Break here.  */
}

int
main (int argc, char *argv[])
{
  if (argc != 3)
    {
      fprintf (stderr, "Usage: %s <first vl> <second vl>\n", argv[0]);
      return 1;
    }

  int first_vl = atoi (argv[1]);
  second_vl = atoi (argv[2]);

  if (first_vl == 0 || second_vl == 0)
    {
      fprintf (stderr, "Invalid vector length.\n");
      return 1;
    }

  /* Set vector length to the first value.  */
  first_vl = set_vl (first_vl);

  printf ("main: first_vl = %d\n", first_vl);

  unsigned char buf[256];

  /* Use an SVE register to make the kernel start saving the SVE bank.  */
  asm volatile ("mov z0.b, #255\n\t"
		"str z0, %0"
		:
		: "m" (buf)
		: "z0", "memory");

  initialize_sve_state_main ();

  struct sigaction sigact;
  sigact.sa_sigaction = sighandler;
  sigact.sa_flags = SA_SIGINFO;
  sigaction (SIGUSR1, &sigact, NULL);

  kill (getpid (), SIGUSR1);

  return 0;
}
