/* { dg-do run { target *-*-linux* *-*-gnu* } } */
/* { dg-options "-fexceptions -fnon-call-exceptions -fasynchronous-unwind-tables -O2" } */
/* Test complex CFA value expressions.  */

#include <unwind.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

static _Unwind_Reason_Code
force_unwind_stop (int version, _Unwind_Action actions,
		   _Unwind_Exception_Class exc_class,
		   struct _Unwind_Exception *exc_obj,
		   struct _Unwind_Context *context,
		   void *stop_parameter)
{
  if (actions & _UA_END_OF_STACK)
    abort ();
  return _URC_NO_REASON;
}

static void
force_unwind ()
{
  struct _Unwind_Exception *exc = malloc (sizeof (*exc));
  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
  exc->exception_cleanup = 0;

  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
  abort ();
}

int count;

static void
counter (void *p __attribute__((unused)))
{
  ++count;
}

static void
handler (void *p __attribute__((unused)))
{
  if (count != 2)
    abort ();
  _exit (0);
}

static void
__attribute__((noinline))
check (intptr_t p)
{
  if ((p & 15) != 0)
    abort ();
}

static int __attribute__((noinline))
fn5 (void)
{
  char dummy __attribute__((cleanup (counter)));
  force_unwind ();
  return 0;
}

void
bar (void)
{
  char dummy __attribute__((cleanup (counter)));
  unsigned long tmp[4] __attribute__((aligned(16)));
  check ((intptr_t) tmp);
  fn5 ();
}

void __attribute__((noinline))
foo (int x)
{
  char buf[256];
#ifdef __i386__
  __asm (
	"testl	%0, %0\n\t"
	"jnz	1f\n\t"
	".subsection 1\n\t"
	".type	_L_mutex_lock_%=, @function\n"
"_L_mutex_lock_%=:\n"
"1:\t"	"leal	%1, %%ecx\n"
"2:\t"	"call	bar\n"
"3:\t"	"jmp	18f\n"
"4:\t"	".size _L_mutex_lock_%=, .-_L_mutex_lock_%=\n\t"
	".previous\n\t"
	".section	.eh_frame,\"a\",@progbits\n"
"5:\t"	".long	7f-6f	# Length of Common Information Entry\n"
"6:\t"	".long	0x0	# CIE Identifier Tag\n\t"
	".byte	0x1	# CIE Version\n\t"
	".ascii \"zR\\0\"	# CIE Augmentation\n\t"
	".uleb128 0x1	# CIE Code Alignment Factor\n\t"
	".sleb128 -4	# CIE Data Alignment Factor\n\t"
	".byte	0x8	# CIE RA Column\n\t"
	".uleb128 0x1	# Augmentation size\n\t"
	".byte	0x1b	# FDE Encoding (pcrel sdata4)\n\t"
	".byte	0xc	# DW_CFA_def_cfa\n\t"
	".uleb128 0x4\n\t"
	".uleb128 0x0\n\t"
	".align 4\n"
"7:\t"	".long	17f-8f	# FDE Length\n"
"8:\t"	".long	8b-5b	# FDE CIE offset\n\t"
	".long	1b-.	# FDE initial location\n\t"
	".long	4b-1b	# FDE address range\n\t"
	".uleb128 0x0	# Augmentation size\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x8\n\t"
	".uleb128 10f-9f\n"
"9:\t"	".byte	0x78	# DW_OP_breg8\n\t"
	".sleb128 3b-1b\n"
"10:\t"	".byte	0x40 + (2b-1b) # DW_CFA_advance_loc\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x8\n\t"
	".uleb128 12f-11f\n"
"11:\t"	".byte	0x78	# DW_OP_breg8\n\t"
	".sleb128 3b-2b\n"
"12:\t"	".byte	0x40 + (3b-2b-1) # DW_CFA_advance_loc\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x8\n\t"
	".uleb128 16f-13f\n"
"13:\t"	".byte	0x78	# DW_OP_breg8\n\t"
	".sleb128 15f-14f\n\t"
	".byte	0x0d	# DW_OP_const4s\n"
"14:\t"	".4byte	3b-.\n\t"
	".byte	0x1c	# DW_OP_minus\n\t"
	".byte	0x0d	# DW_OP_const4s\n"
"15:\t"	".4byte	18f-.\n\t"
	".byte	0x22	# DW_OP_plus\n"
"16:\t"	".align 4\n"
"17:\t"	".previous\n"
"18:"
	: : "r" (x), "m" (x), "r" (buf)
	: "memory", "eax", "edx", "ecx");
#elif defined __x86_64__
  __asm (
	"testl	%0, %0\n\t"
	"jnz	1f\n\t"
	".subsection 1\n\t"
	".type	_L_mutex_lock_%=, @function\n"
"_L_mutex_lock_%=:\n"
"1:\t"	"leaq	%1, %%rdi\n"
"2:\t"	"subq	$136, %%rsp\n"
"3:\t"	"call	bar\n"
"4:\t"	"addq	$136, %%rsp\n"
"5:\t"	"jmp	24f\n"
"6:\t"	".size _L_mutex_lock_%=, .-_L_mutex_lock_%=\n\t"
	".previous\n\t"
	".section	.eh_frame,\"a\",@progbits\n"
"7:\t"	".long	9f-8f	# Length of Common Information Entry\n"
"8:\t"	".long	0x0	# CIE Identifier Tag\n\t"
	".byte	0x1	# CIE Version\n\t"
	".ascii \"zR\\0\"	# CIE Augmentation\n\t"
	".uleb128 0x1	# CIE Code Alignment Factor\n\t"
	".sleb128 -8	# CIE Data Alignment Factor\n\t"
	".byte	0x10	# CIE RA Column\n\t"
	".uleb128 0x1	# Augmentation size\n\t"
	".byte	0x1b	# FDE Encoding (pcrel sdata4)\n\t"
	".byte	0x12	# DW_CFA_def_cfa_sf\n\t"
	".uleb128 0x7\n\t"
	".sleb128 16\n\t"
	".align 8\n"
"9:\t"	".long	23f-10f	# FDE Length\n"
"10:\t"	".long	10b-7b	# FDE CIE offset\n\t"
	".long	1b-.	# FDE initial location\n\t"
	".long	6b-1b	# FDE address range\n\t"
	".uleb128 0x0	# Augmentation size\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x10\n\t"
	".uleb128 12f-11f\n"
"11:\t"	".byte	0x80	# DW_OP_breg16\n\t"
	".sleb128 4b-1b\n"
"12:\t"	".byte	0x40 + (2b-1b) # DW_CFA_advance_loc\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x10\n\t"
	".uleb128 14f-13f\n"
"13:\t"	".byte	0x80	# DW_OP_breg16\n\t"
	".sleb128 4b-2b\n"
"14:\t"	".byte	0x40 + (3b-2b) # DW_CFA_advance_loc\n\t"
	".byte	0x0e	# DW_CFA_def_cfa_offset\n\t"
	".uleb128 0\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x10\n\t"
	".uleb128 16f-15f\n"
"15:\t"	".byte	0x80	# DW_OP_breg16\n\t"
	".sleb128 4b-3b\n"
"16:\t"	".byte	0x40 + (4b-3b-1) # DW_CFA_advance_loc\n\t"
	".byte	0x0e	# DW_CFA_def_cfa_offset\n\t"
	".uleb128 136\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x10\n\t"
	".uleb128 20f-17f\n"
"17:\t"	".byte	0x80	# DW_OP_breg16\n\t"
	".sleb128 19f-18f\n\t"
	".byte	0x0d	# DW_OP_const4s\n"
"18:\t"	".4byte	4b-.\n\t"
	".byte	0x1c	# DW_OP_minus\n\t"
	".byte	0x0d	# DW_OP_const4s\n"
"19:\t"	".4byte	24f-.\n\t"
	".byte	0x22	# DW_OP_plus\n"
"20:\t"	".byte	0x40 + (5b-4b+1) # DW_CFA_advance_loc\n\t"
	".byte	0x13	# DW_CFA_def_cfa_offset_sf\n\t"
	".sleb128 16\n\t"
	".byte	0x16	# DW_CFA_val_expression\n\t"
	".uleb128 0x10\n\t"
	".uleb128 22f-21f\n"
"21:\t"	".byte	0x80	# DW_OP_breg16\n\t"
	".sleb128 4b-5b\n"
"22:\t"	".align 8\n"
"23:\t"	".previous\n"
"24:"
	: : "r" (x), "m" (x), "r" (buf)
	: "memory", "rax", "rdx", "rcx", "rsi", "rdi",
	  "r8", "r9", "r10", "r11");
#else
# error Unsupported test architecture
#endif
}

static int __attribute__((noinline))
fn2 (void)
{
  foo (3);
  return 0;
}

static int __attribute__((noinline))
fn1 (void)
{
  fn2 ();
  return 0;
}

static void *
fn0 (void)
{
  char dummy __attribute__((cleanup (handler)));
  fn1 ();
  return 0;
}

int
main (void)
{
  fn0 ();
  return 0;
}
