diff mbox series

[2/2] aarch64: Fix ABI handling of aligned enums [PR109661]

Message ID mptv8h9tl72.fsf@arm.com
State New
Headers show
Series [1/2] aarch64: Rename abi_break parameters [PR109661] | expand

Commit Message

Richard Sandiford May 3, 2023, 4:48 p.m. UTC
aarch64_function_arg_alignment has traditionally taken the alignment
of a scalar type T from TYPE_ALIGN (TYPE_MAIN_VARIANT (T)).  This is
supposed to discard any user alignment and give the alignment of the
underlying fundamental type.

PR109661 shows that this did the wrong thing for enums with
a defined underlying type, because:

(1) The enum itself could be aligned, using attributes.
(2) The enum would pick up any user alignment on the underlying type.

We get the right behaviour if we look at the TYPE_MAIN_VARIANT
of the underlying type instead.

As always, this affects register and stack arguments differently,
because:

(a) The code that handles register arguments only considers the
    alignment of types that occupy two registers, whereas the
    stack alignment is applied regardless of size.

(b) The code that handles register arguments tests the alignment
    for equality with 16 bytes, so that (unexpected) greater alignments
    are ignored.  The code that handles stack arguments instead caps the
    alignment to 16 bytes.

There is now (since GCC 13) an assert to trap the difference between
(a) and (b), which is how the new incompatiblity showed up.

Clang alredy handled the testcases correctly, so this patch aligns
the GCC behaviour with the Clang behaviour.

I'm planning to remove the asserts on the branches, since we don't
want to change the ABI there.

Tested on aarch64-linux-gnu & pushed.

Richard


gcc/
	PR target/109661
	* config/aarch64/aarch64.cc (aarch64_function_arg_alignment): Add
	a new ABI break parameter for GCC 14.  Set it to the alignment
	of enums that have an underlying type.  Take the true alignment
	of such enums from the TYPE_ALIGN of the underlying type's
	TYPE_MAIN_VARIANT.
	(aarch64_function_arg_boundary): Update accordingly.
	(aarch64_layout_arg, aarch64_gimplify_va_arg_expr): Likewise.
	Warn about ABI differences.

gcc/testsuite/
	* g++.target/aarch64/pr109661-1.C: New test.
	* g++.target/aarch64/pr109661-2.C: Likewise.
	* g++.target/aarch64/pr109661-3.C: Likewise.
	* g++.target/aarch64/pr109661-4.C: Likewise.
	* gcc.target/aarch64/pr109661-1.c: Likewise.
---
 gcc/config/aarch64/aarch64.cc                 |  43 ++-
 gcc/testsuite/g++.target/aarch64/pr109661-1.C | 253 ++++++++++++++++++
 gcc/testsuite/g++.target/aarch64/pr109661-2.C | 253 ++++++++++++++++++
 gcc/testsuite/g++.target/aarch64/pr109661-3.C | 253 ++++++++++++++++++
 gcc/testsuite/g++.target/aarch64/pr109661-4.C | 253 ++++++++++++++++++
 gcc/testsuite/gcc.target/aarch64/pr109661-1.c |  11 +
 6 files changed, 1061 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/aarch64/pr109661-1.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/pr109661-2.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/pr109661-3.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/pr109661-4.C
 create mode 100644 gcc/testsuite/gcc.target/aarch64/pr109661-1.c
diff mbox series

Patch

diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 70916ad63d2..546cb121331 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -7467,17 +7467,21 @@  aarch64_vfp_is_call_candidate (cumulative_args_t pcum_v, machine_mode mode,
    4.1).  ABI_BREAK_GCC_9 is set to the old alignment if the alignment
    was incorrectly calculated in versions of GCC prior to GCC 9.
    ABI_BREAK_GCC_13 is set to the old alignment if it was incorrectly
-   calculated in versions between GCC 9 and GCC 13.
+   calculated in versions between GCC 9 and GCC 13.  If the alignment
+   might have changed between GCC 13 and GCC 14, ABI_BREAK_GCC_14
+   is the old GCC 13 alignment, otherwise it is zero.
 
    This is a helper function for local use only.  */
 
 static unsigned int
 aarch64_function_arg_alignment (machine_mode mode, const_tree type,
 				unsigned int *abi_break_gcc_9,
-				unsigned int *abi_break_gcc_13)
+				unsigned int *abi_break_gcc_13,
+				unsigned int *abi_break_gcc_14)
 {
   *abi_break_gcc_9 = 0;
   *abi_break_gcc_13 = 0;
+  *abi_break_gcc_14 = 0;
   if (!type)
     return GET_MODE_ALIGNMENT (mode);
 
@@ -7498,6 +7502,11 @@  aarch64_function_arg_alignment (machine_mode mode, const_tree type,
 	  gcc_assert (known_eq (POINTER_SIZE, GET_MODE_BITSIZE (mode)));
 	  return POINTER_SIZE;
 	}
+      if (TREE_CODE (type) == ENUMERAL_TYPE && TREE_TYPE (type))
+	{
+	  *abi_break_gcc_14 = TYPE_ALIGN (type);
+	  type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+	}
       gcc_assert (!TYPE_USER_ALIGN (type));
       return TYPE_ALIGN (type);
     }
@@ -7576,6 +7585,7 @@  aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
   HOST_WIDE_INT size;
   unsigned int abi_break_gcc_9;
   unsigned int abi_break_gcc_13;
+  unsigned int abi_break_gcc_14;
 
   /* We need to do this once per argument.  */
   if (pcum->aapcs_arg_processed)
@@ -7715,7 +7725,7 @@  aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
 
   unsigned int alignment
     = aarch64_function_arg_alignment (mode, type, &abi_break_gcc_9,
-				      &abi_break_gcc_13);
+				      &abi_break_gcc_13, &abi_break_gcc_14);
 
   gcc_assert ((allocate_nvrn || alignment <= 16 * BITS_PER_UNIT)
 	      && (!alignment || abi_break_gcc_9 < alignment)
@@ -7799,6 +7809,13 @@  aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
 	    inform (input_location, "parameter passing for argument of type "
 		    "%qT changed in GCC 13.1", type);
 
+	  if (warn_pcs_change
+	      && abi_break_gcc_14
+	      && ((abi_break_gcc_14 == 16 * BITS_PER_UNIT)
+		  != (alignment == 16 * BITS_PER_UNIT)))
+	    inform (input_location, "parameter passing for argument of type "
+		    "%qT changed in GCC 14.1", type);
+
 	  /* The == 16 * BITS_PER_UNIT instead of >= 16 * BITS_PER_UNIT
 	     comparison is there because for > 16 * BITS_PER_UNIT
 	     alignment nregs should be > 2 and therefore it should be
@@ -7870,6 +7887,13 @@  on_stack:
     inform (input_location, "parameter passing for argument of type "
 	    "%qT changed in GCC 13.1", type);
 
+  if (warn_pcs_change
+      && abi_break_gcc_14
+      && ((abi_break_gcc_14 >= 16 * BITS_PER_UNIT)
+	  != (alignment >= 16 * BITS_PER_UNIT)))
+    inform (input_location, "parameter passing for argument of type "
+	    "%qT changed in GCC 14.1", type);
+
   if (alignment == 16 * BITS_PER_UNIT)
     {
       int new_size = ROUND_UP (pcum->aapcs_stack_size, 16 / UNITS_PER_WORD);
@@ -7994,9 +8018,11 @@  aarch64_function_arg_boundary (machine_mode mode, const_tree type)
 {
   unsigned int abi_break_gcc_9;
   unsigned int abi_break_gcc_13;
+  unsigned int abi_break_gcc_14;
   unsigned int alignment = aarch64_function_arg_alignment (mode, type,
 							   &abi_break_gcc_9,
-							   &abi_break_gcc_13);
+							   &abi_break_gcc_13,
+							   &abi_break_gcc_14);
   /* We rely on aarch64_layout_arg and aarch64_gimplify_va_arg_expr
      to emit warnings about ABI incompatibility.  */
   alignment = MIN (MAX (alignment, PARM_BOUNDARY), STACK_BOUNDARY);
@@ -19765,9 +19791,10 @@  aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
   unsigned int abi_break_gcc_9;
   unsigned int abi_break_gcc_13;
+  unsigned int abi_break_gcc_14;
   align
     = aarch64_function_arg_alignment (mode, type, &abi_break_gcc_9,
-				      &abi_break_gcc_13)
+				      &abi_break_gcc_13, &abi_break_gcc_14)
     / BITS_PER_UNIT;
 
   dw_align = false;
@@ -19815,6 +19842,12 @@  aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 	inform (input_location, "parameter passing for argument of type "
 		"%qT changed in GCC 13.1", type);
 
+      if (warn_psabi
+	  && abi_break_gcc_14
+	  && (abi_break_gcc_14 > 8 * BITS_PER_UNIT) != (align > 8))
+	inform (input_location, "parameter passing for argument of type "
+		"%qT changed in GCC 14.1", type);
+
       if (align > 8)
 	{
 	  if (abi_break_gcc_9 && warn_psabi)
diff --git a/gcc/testsuite/g++.target/aarch64/pr109661-1.C b/gcc/testsuite/g++.target/aarch64/pr109661-1.C
new file mode 100644
index 00000000000..d668a544fa7
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/pr109661-1.C
@@ -0,0 +1,253 @@ 
+/* { dg-options "-O2 -Wpsabi" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <stdarg.h>
+
+#define ALIGN
+
+typedef __uint128_t u128_4 __attribute__((aligned(4)));
+typedef __uint128_t u128_8 __attribute__((aligned(8)));
+typedef __uint128_t u128_16 __attribute__((aligned(16)));
+typedef __uint128_t u128_32 __attribute__((aligned(32)));
+typedef __uint128_t u128;
+
+typedef __UINT64_TYPE__ u64_4 __attribute__((aligned(4)));
+typedef __UINT64_TYPE__ u64_8 __attribute__((aligned(8)));
+typedef __UINT64_TYPE__ u64_16 __attribute__((aligned(16)));
+typedef __UINT64_TYPE__ u64_32 __attribute__((aligned(32)));
+typedef __UINT64_TYPE__ u64;
+
+enum class ALIGN e128_4 : u128_4 { A };
+enum class ALIGN e128_8 : u128_8 { A };
+enum class ALIGN e128_16 : u128_16 { A };
+enum class ALIGN e128_32 : u128_32 { A };
+enum class ALIGN e128 : u128 { A };
+
+enum class ALIGN e64_4 : u64_4 { A };
+enum class ALIGN e64_8 : u64_8 { A };
+enum class ALIGN e64_16 : u64_16 { A };
+enum class ALIGN e64_32 : u64_32 { A };
+enum class ALIGN e64 : u64 { A };
+
+extern "C" {
+
+/*
+** reg_e128_4:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_4 reg_e128_4 (int x, e128_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+
+/*
+** reg_e128_8:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_8 reg_e128_8 (int x, e128_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+
+/*
+** reg_e128_16:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_16 reg_e128_16 (int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e128_32:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_32 reg_e128_32 (int x, e128_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+
+/*
+** reg_e128:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128 reg_e128 (int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_4:
+**	mov	x0, x1
+**	ret
+*/
+e64_4 reg_e64_4 (int x, e64_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_8:
+**	mov	x0, x1
+**	ret
+*/
+e64_8 reg_e64_8 (int x, e64_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_16:
+**	mov	x0, x1
+**	ret
+*/
+e64_16 reg_e64_16 (int x, e64_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_32:
+**	mov	x0, x1
+**	ret
+*/
+e64_32 reg_e64_32 (int x, e64_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64:
+**	mov	x0, x1
+**	ret
+*/
+e64 reg_e64 (int x, e64 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_4:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_4 stack_e128_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+
+/*
+** stack_e128_8:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_8 stack_e128_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+
+/*
+** stack_e128_16:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_16 stack_e128_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_32:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_32 stack_e128_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128 stack_e128 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_4:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_4 stack_e64_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_8:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_8 stack_e64_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_16:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_16 stack_e64_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_16 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+
+/*
+** stack_e64_32:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_32 stack_e64_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+
+/*
+** stack_e64:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64 stack_e64 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+void callee (int n, ...);
+
+void
+caller ()
+{
+  callee (1, e128_4::A); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  callee (1, e128_8::A); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  callee (1, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e128_32::A); /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+  callee (1, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (1, e64_4::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_8::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_32::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64::A); /* { dg-bogus {parameter passing} } */
+
+#define LOTS 1, 2, 3, 4, 5, 6, 7, 8, 9
+
+  callee (LOTS, e128_4::A); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  callee (LOTS, e128_8::A); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  callee (LOTS, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_32::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (LOTS, e64_4::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e64_8::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e64_16::A); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  callee (LOTS, e64_32::A); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  callee (LOTS, e64::A); /* { dg-bogus {parameter passing} } */
+}
+
+void
+va (volatile void *ptr, ...)
+{
+  va_list ap;
+  va_start (ap, ptr);
+  *(volatile e128_4 *) ptr = va_arg (ap, e128_4); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  *(volatile e128_8 *) ptr = va_arg (ap, e128_8); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  *(volatile e128_16 *) ptr = va_arg (ap, e128_16); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_32 *) ptr = va_arg (ap, e128_32); /* { dg-bogus {parameter passing} } */
+  *(volatile e128 *) ptr = va_arg (ap, e128); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_4 *) ptr = va_arg (ap, e64_4); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_8 *) ptr = va_arg (ap, e64_8); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_16 *) ptr = va_arg (ap, e64_16); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  *(volatile e64_32 *) ptr = va_arg (ap, e64_32); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  *(volatile e64 *) ptr = va_arg (ap, e64); /* { dg-bogus {parameter passing} } */
+}
+
+}
diff --git a/gcc/testsuite/g++.target/aarch64/pr109661-2.C b/gcc/testsuite/g++.target/aarch64/pr109661-2.C
new file mode 100644
index 00000000000..f081417da95
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/pr109661-2.C
@@ -0,0 +1,253 @@ 
+/* { dg-options "-O2 -Wpsabi" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <stdarg.h>
+
+#define ALIGN __attribute__((aligned(8)))
+
+typedef __uint128_t u128_4 __attribute__((aligned(4)));
+typedef __uint128_t u128_8 __attribute__((aligned(8)));
+typedef __uint128_t u128_16 __attribute__((aligned(16)));
+typedef __uint128_t u128_32 __attribute__((aligned(32)));
+typedef __uint128_t u128;
+
+typedef __UINT64_TYPE__ u64_4 __attribute__((aligned(4)));
+typedef __UINT64_TYPE__ u64_8 __attribute__((aligned(8)));
+typedef __UINT64_TYPE__ u64_16 __attribute__((aligned(16)));
+typedef __UINT64_TYPE__ u64_32 __attribute__((aligned(32)));
+typedef __UINT64_TYPE__ u64;
+
+enum class ALIGN e128_4 : u128_4 { A };
+enum class ALIGN e128_8 : u128_8 { A };
+enum class ALIGN e128_16 : u128_16 { A };
+enum class ALIGN e128_32 : u128_32 { A };
+enum class ALIGN e128 : u128 { A };
+
+enum class ALIGN e64_4 : u64_4 { A };
+enum class ALIGN e64_8 : u64_8 { A };
+enum class ALIGN e64_16 : u64_16 { A };
+enum class ALIGN e64_32 : u64_32 { A };
+enum class ALIGN e64 : u64 { A };
+
+extern "C" {
+
+/*
+** reg_e128_4:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_4 reg_e128_4 (int x, e128_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+
+/*
+** reg_e128_8:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_8 reg_e128_8 (int x, e128_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+
+/*
+** reg_e128_16:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_16 reg_e128_16 (int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e128_32:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_32 reg_e128_32 (int x, e128_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+
+/*
+** reg_e128:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128 reg_e128 (int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_4:
+**	mov	x0, x1
+**	ret
+*/
+e64_4 reg_e64_4 (int x, e64_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_8:
+**	mov	x0, x1
+**	ret
+*/
+e64_8 reg_e64_8 (int x, e64_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_16:
+**	mov	x0, x1
+**	ret
+*/
+e64_16 reg_e64_16 (int x, e64_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_32:
+**	mov	x0, x1
+**	ret
+*/
+e64_32 reg_e64_32 (int x, e64_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64:
+**	mov	x0, x1
+**	ret
+*/
+e64 reg_e64 (int x, e64 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_4:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_4 stack_e128_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+
+/*
+** stack_e128_8:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_8 stack_e128_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+
+/*
+** stack_e128_16:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_16 stack_e128_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_32:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_32 stack_e128_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128 stack_e128 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_4:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_4 stack_e64_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_8:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_8 stack_e64_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_16:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_16 stack_e64_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_16 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+
+/*
+** stack_e64_32:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_32 stack_e64_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+
+/*
+** stack_e64:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64 stack_e64 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+void callee (int n, ...);
+
+void
+caller ()
+{
+  callee (1, e128_4::A); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  callee (1, e128_8::A); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  callee (1, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e128_32::A); /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+  callee (1, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (1, e64_4::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_8::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_32::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64::A); /* { dg-bogus {parameter passing} } */
+
+#define LOTS 1, 2, 3, 4, 5, 6, 7, 8, 9
+
+  callee (LOTS, e128_4::A); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  callee (LOTS, e128_8::A); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  callee (LOTS, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_32::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (LOTS, e64_4::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e64_8::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e64_16::A); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  callee (LOTS, e64_32::A); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  callee (LOTS, e64::A); /* { dg-bogus {parameter passing} } */
+}
+
+void
+va (volatile void *ptr, ...)
+{
+  va_list ap;
+  va_start (ap, ptr);
+  *(volatile e128_4 *) ptr = va_arg (ap, e128_4); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  *(volatile e128_8 *) ptr = va_arg (ap, e128_8); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  *(volatile e128_16 *) ptr = va_arg (ap, e128_16); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_32 *) ptr = va_arg (ap, e128_32); /* { dg-bogus {parameter passing} } */
+  *(volatile e128 *) ptr = va_arg (ap, e128); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_4 *) ptr = va_arg (ap, e64_4); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_8 *) ptr = va_arg (ap, e64_8); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_16 *) ptr = va_arg (ap, e64_16); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  *(volatile e64_32 *) ptr = va_arg (ap, e64_32); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  *(volatile e64 *) ptr = va_arg (ap, e64); /* { dg-bogus {parameter passing} } */
+}
+
+}
diff --git a/gcc/testsuite/g++.target/aarch64/pr109661-3.C b/gcc/testsuite/g++.target/aarch64/pr109661-3.C
new file mode 100644
index 00000000000..dab2fb295d3
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/pr109661-3.C
@@ -0,0 +1,253 @@ 
+/* { dg-options "-O2 -Wpsabi" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <stdarg.h>
+
+#define ALIGN __attribute__((aligned(16)))
+
+typedef __uint128_t u128_4 __attribute__((aligned(4)));
+typedef __uint128_t u128_8 __attribute__((aligned(8)));
+typedef __uint128_t u128_16 __attribute__((aligned(16)));
+typedef __uint128_t u128_32 __attribute__((aligned(32)));
+typedef __uint128_t u128;
+
+typedef __UINT64_TYPE__ u64_4 __attribute__((aligned(4)));
+typedef __UINT64_TYPE__ u64_8 __attribute__((aligned(8)));
+typedef __UINT64_TYPE__ u64_16 __attribute__((aligned(16)));
+typedef __UINT64_TYPE__ u64_32 __attribute__((aligned(32)));
+typedef __UINT64_TYPE__ u64;
+
+enum class ALIGN e128_4 : u128_4 { A };
+enum class ALIGN e128_8 : u128_8 { A };
+enum class ALIGN e128_16 : u128_16 { A };
+enum class ALIGN e128_32 : u128_32 { A };
+enum class ALIGN e128 : u128 { A };
+
+enum class ALIGN e64_4 : u64_4 { A };
+enum class ALIGN e64_8 : u64_8 { A };
+enum class ALIGN e64_16 : u64_16 { A };
+enum class ALIGN e64_32 : u64_32 { A };
+enum class ALIGN e64 : u64 { A };
+
+extern "C" {
+
+/*
+** reg_e128_4:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_4 reg_e128_4 (int x, e128_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e128_8:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_8 reg_e128_8 (int x, e128_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e128_16:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_16 reg_e128_16 (int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e128_32:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_32 reg_e128_32 (int x, e128_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+
+/*
+** reg_e128:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128 reg_e128 (int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_4:
+**	mov	x0, x1
+**	ret
+*/
+e64_4 reg_e64_4 (int x, e64_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_8:
+**	mov	x0, x1
+**	ret
+*/
+e64_8 reg_e64_8 (int x, e64_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_16:
+**	mov	x0, x1
+**	ret
+*/
+e64_16 reg_e64_16 (int x, e64_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_32:
+**	mov	x0, x1
+**	ret
+*/
+e64_32 reg_e64_32 (int x, e64_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64:
+**	mov	x0, x1
+**	ret
+*/
+e64 reg_e64 (int x, e64 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_4:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_4 stack_e128_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_8:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_8 stack_e128_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_16:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_16 stack_e128_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_32:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_32 stack_e128_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128 stack_e128 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_4:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_4 stack_e64_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_4' changed in GCC 14.1} } */
+
+/*
+** stack_e64_8:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_8 stack_e64_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_8' changed in GCC 14.1} } */
+
+/*
+** stack_e64_16:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_16 stack_e64_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_16 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+
+/*
+** stack_e64_32:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_32 stack_e64_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+
+/*
+** stack_e64:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64 stack_e64 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64' changed in GCC 14.1} } */
+
+void callee (int n, ...);
+
+void
+caller ()
+{
+  callee (1, e128_4::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e128_8::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e128_32::A); /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+  callee (1, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (1, e64_4::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_8::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_32::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64::A); /* { dg-bogus {parameter passing} } */
+
+#define LOTS 1, 2, 3, 4, 5, 6, 7, 8, 9
+
+  callee (LOTS, e128_4::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_8::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_32::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (LOTS, e64_4::A); /* { dg-note {parameter passing for argument of type 'e64_4' changed in GCC 14.1} } */
+  callee (LOTS, e64_8::A); /* { dg-note {parameter passing for argument of type 'e64_8' changed in GCC 14.1} } */
+  callee (LOTS, e64_16::A); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  callee (LOTS, e64_32::A); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  callee (LOTS, e64::A); /* { dg-note {parameter passing for argument of type 'e64' changed in GCC 14.1} } */
+}
+
+void
+va (volatile void *ptr, ...)
+{
+  va_list ap;
+  va_start (ap, ptr);
+  *(volatile e128_4 *) ptr = va_arg (ap, e128_4); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_8 *) ptr = va_arg (ap, e128_8); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_16 *) ptr = va_arg (ap, e128_16); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_32 *) ptr = va_arg (ap, e128_32); /* { dg-bogus {parameter passing} } */
+  *(volatile e128 *) ptr = va_arg (ap, e128); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_4 *) ptr = va_arg (ap, e64_4); /* { dg-note {parameter passing for argument of type 'e64_4' changed in GCC 14.1} } */
+  *(volatile e64_8 *) ptr = va_arg (ap, e64_8); /* { dg-note {parameter passing for argument of type 'e64_8' changed in GCC 14.1} } */
+  *(volatile e64_16 *) ptr = va_arg (ap, e64_16); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  *(volatile e64_32 *) ptr = va_arg (ap, e64_32); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  *(volatile e64 *) ptr = va_arg (ap, e64); /* { dg-note {parameter passing for argument of type 'e64' changed in GCC 14.1} } */
+}
+
+}
diff --git a/gcc/testsuite/g++.target/aarch64/pr109661-4.C b/gcc/testsuite/g++.target/aarch64/pr109661-4.C
new file mode 100644
index 00000000000..d87e60fcc5d
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/pr109661-4.C
@@ -0,0 +1,253 @@ 
+/* { dg-options "-O2 -Wpsabi" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <stdarg.h>
+
+#define ALIGN __attribute__((aligned(32)))
+
+typedef __uint128_t u128_4 __attribute__((aligned(4)));
+typedef __uint128_t u128_8 __attribute__((aligned(8)));
+typedef __uint128_t u128_16 __attribute__((aligned(16)));
+typedef __uint128_t u128_32 __attribute__((aligned(32)));
+typedef __uint128_t u128;
+
+typedef __UINT64_TYPE__ u64_4 __attribute__((aligned(4)));
+typedef __UINT64_TYPE__ u64_8 __attribute__((aligned(8)));
+typedef __UINT64_TYPE__ u64_16 __attribute__((aligned(16)));
+typedef __UINT64_TYPE__ u64_32 __attribute__((aligned(32)));
+typedef __UINT64_TYPE__ u64;
+
+enum class ALIGN e128_4 : u128_4 { A };
+enum class ALIGN e128_8 : u128_8 { A };
+enum class ALIGN e128_16 : u128_16 { A };
+enum class ALIGN e128_32 : u128_32 { A };
+enum class ALIGN e128 : u128 { A };
+
+enum class ALIGN e64_4 : u64_4 { A };
+enum class ALIGN e64_8 : u64_8 { A };
+enum class ALIGN e64_16 : u64_16 { A };
+enum class ALIGN e64_32 : u64_32 { A };
+enum class ALIGN e64 : u64 { A };
+
+extern "C" {
+
+/*
+** reg_e128_4:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_4 reg_e128_4 (int x, e128_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+
+/*
+** reg_e128_8:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_8 reg_e128_8 (int x, e128_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+
+/*
+** reg_e128_16:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_16 reg_e128_16 (int x, e128_16 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_16' changed in GCC 14.1} } */
+
+/*
+** reg_e128_32:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128_32 reg_e128_32 (int x, e128_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+
+/*
+** reg_e128:
+** (
+**	mov	x1, x3
+**	mov	x0, x2
+** |
+**	mov	x0, x2
+**	mov	x1, x3
+** )
+**	ret
+*/
+e128 reg_e128 (int x, e128 y) { return y; } /* { dg-note {parameter passing for argument of type 'e128' changed in GCC 14.1} } */
+
+/*
+** reg_e64_4:
+**	mov	x0, x1
+**	ret
+*/
+e64_4 reg_e64_4 (int x, e64_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_8:
+**	mov	x0, x1
+**	ret
+*/
+e64_8 reg_e64_8 (int x, e64_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_16:
+**	mov	x0, x1
+**	ret
+*/
+e64_16 reg_e64_16 (int x, e64_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64_32:
+**	mov	x0, x1
+**	ret
+*/
+e64_32 reg_e64_32 (int x, e64_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** reg_e64:
+**	mov	x0, x1
+**	ret
+*/
+e64 reg_e64 (int x, e64 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_4:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_4 stack_e128_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_4 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_8:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_8 stack_e128_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_8 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_16:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_16 stack_e128_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_16 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128_32:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128_32 stack_e128_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128_32 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e128:
+**	ldp	x0, x1, \[sp, #?16\]
+**	ret
+*/
+e128 stack_e128 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e128 y) { return y; } /* { dg-bogus {parameter passing} } */
+
+/*
+** stack_e64_4:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_4 stack_e64_4 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_4 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_4' changed in GCC 14.1} } */
+
+/*
+** stack_e64_8:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_8 stack_e64_8 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_8 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_8' changed in GCC 14.1} } */
+
+/*
+** stack_e64_16:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_16 stack_e64_16 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_16 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+
+/*
+** stack_e64_32:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64_32 stack_e64_32 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64_32 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+
+/*
+** stack_e64:
+**	ldr	x0, \[sp, #?8\]
+**	ret
+*/
+e64 stack_e64 (u128 x0, u128 x2, u128 x4, u128 x6, int x, e64 y) { return y; } /* { dg-note {parameter passing for argument of type 'e64' changed in GCC 14.1} } */
+
+void callee (int n, ...);
+
+void
+caller ()
+{
+  callee (1, e128_4::A); /* { dg-note {parameter passing for argument of type 'e128_4' changed in GCC 14.1} } */
+  callee (1, e128_8::A); /* { dg-note {parameter passing for argument of type 'e128_8' changed in GCC 14.1} } */
+  callee (1, e128_16::A); /* { dg-note {parameter passing for argument of type 'e128_16' changed in GCC 14.1} } */
+  callee (1, e128_32::A); /* { dg-note {parameter passing for argument of type 'e128_32' changed in GCC 14.1} } */
+  callee (1, e128::A); /* { dg-note {parameter passing for argument of type 'e128' changed in GCC 14.1} } */
+
+  callee (1, e64_4::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_8::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_16::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64_32::A); /* { dg-bogus {parameter passing} } */
+  callee (1, e64::A); /* { dg-bogus {parameter passing} } */
+
+#define LOTS 1, 2, 3, 4, 5, 6, 7, 8, 9
+
+  callee (LOTS, e128_4::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_8::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_16::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128_32::A); /* { dg-bogus {parameter passing} } */
+  callee (LOTS, e128::A); /* { dg-bogus {parameter passing} } */
+
+  callee (LOTS, e64_4::A); /* { dg-note {parameter passing for argument of type 'e64_4' changed in GCC 14.1} } */
+  callee (LOTS, e64_8::A); /* { dg-note {parameter passing for argument of type 'e64_8' changed in GCC 14.1} } */
+  callee (LOTS, e64_16::A); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  callee (LOTS, e64_32::A); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  callee (LOTS, e64::A); /* { dg-note {parameter passing for argument of type 'e64' changed in GCC 14.1} } */
+}
+
+void
+va (volatile void *ptr, ...)
+{
+  va_list ap;
+  va_start (ap, ptr);
+  *(volatile e128_4 *) ptr = va_arg (ap, e128_4); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_8 *) ptr = va_arg (ap, e128_8); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_16 *) ptr = va_arg (ap, e128_16); /* { dg-bogus {parameter passing} } */
+  *(volatile e128_32 *) ptr = va_arg (ap, e128_32); /* { dg-bogus {parameter passing} } */
+  *(volatile e128 *) ptr = va_arg (ap, e128); /* { dg-bogus {parameter passing} } */
+  *(volatile e64_4 *) ptr = va_arg (ap, e64_4); /* { dg-note {parameter passing for argument of type 'e64_4' changed in GCC 14.1} } */
+  *(volatile e64_8 *) ptr = va_arg (ap, e64_8); /* { dg-note {parameter passing for argument of type 'e64_8' changed in GCC 14.1} } */
+  *(volatile e64_16 *) ptr = va_arg (ap, e64_16); /* { dg-note {parameter passing for argument of type 'e64_16' changed in GCC 14.1} } */
+  *(volatile e64_32 *) ptr = va_arg (ap, e64_32); /* { dg-note {parameter passing for argument of type 'e64_32' changed in GCC 14.1} } */
+  *(volatile e64 *) ptr = va_arg (ap, e64); /* { dg-note {parameter passing for argument of type 'e64' changed in GCC 14.1} } */
+}
+
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/pr109661-1.c b/gcc/testsuite/gcc.target/aarch64/pr109661-1.c
new file mode 100644
index 00000000000..5686b2b1cb5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr109661-1.c
@@ -0,0 +1,11 @@ 
+/* { dg-options "-O2 -Wpsabi" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+enum __attribute__((aligned(16))) e { E };
+
+/*
+** test:
+**	mov	w0, w1
+**	ret
+*/
+enum e test (int x, enum e y) { return y; } /* { dg-bogus {parameter passing} } */