diff mbox series

[committed] aarch64: Fix parameter passing for [[no_unique_address]]

Message ID mptd07qwr5n.fsf@arm.com
State New
Headers show
Series [committed] aarch64: Fix parameter passing for [[no_unique_address]] | expand

Commit Message

Richard Sandiford April 29, 2020, 9:57 a.m. UTC
This patch makes the ABI code ignore zero-sized [[no_unique_address]]
fields when deciding whether something is a HFA or HVA.

As things stood, we'd get two sets of -Wpsabi warnings, one when
trying to decide whether something was an SVE function, and another
when actually processing the function definition or function call.
The patch therefore makes aapcs_vfp_sub_candidate honour the
CUMULATIVE_ARGS "silent_p" flag where applicable.

This doesn't stop all duplicate warnings for parameters, and I suspect
we'll get duplicate warnings for return values too, but it should be
better than nothing.

Clang produces equivalent code for all tests except unions Q and R,
which clang passes in s0 rather than w0.  I think this is a clang bug,
since the "x" field contains a single-byte FDT and so shouldn't be
ignored.

Tested on aarch64-linux-gnu and aarch64_be-elf.  Pushed.

Richard


2020-04-29  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* config/aarch64/aarch64.c (aarch64_function_arg_alignment): Add a
	comment explaining why we consider even zero-sized fields.
	(WARN_PSABI_EMPTY_CXX17_BASE): New constant.
	(WARN_PSABI_NO_UNIQUE_ADDRESS): Likewise.
	(aapcs_vfp_sub_candidate): Replace the boolean pointer parameter
	avoid_cxx17_empty_base with a pointer to a bitmask.  Ignore fields
	whose DECL_FIELD_ABI_IGNORED bit is set when determining whether
	something actually is a HFA or HVA.  Record whether we see a
	[[no_unique_address]] field that previous GCCs would not have
	ignored in this way.
	(aarch64_vfp_is_call_or_return_candidate): Add a parameter to say
	whether diagnostics should be suppressed.  Update the calls to
	aapcs_vfp_sub_candidate and report a -Wpsabi warning for the
	[[no_unique_address]] case.
	(aarch64_return_in_msb): Update call accordingly, never silencing
	diagnostics.
	(aarch64_function_value): Likewise.
	(aarch64_return_in_memory_1): Likewise.
	(aarch64_init_cumulative_args): Likewise.
	(aarch64_gimplify_va_arg_expr): Likewise.
	(aarch64_pass_by_reference_1): Take a CUMULATIVE_ARGS pointer and
	use it to decide whether arch64_vfp_is_call_or_return_candidate
	should be silent.
	(aarch64_pass_by_reference): Update calls accordingly.
	(aarch64_vfp_is_call_candidate): Use the CUMULATIVE_ARGS argument
	to decide whether arch64_vfp_is_call_or_return_candidate should be
	silent.

gcc/testsuite/
	* g++.target/aarch64/no_unique_address_1.C: New test.
	* g++.target/aarch64/no_unique_address_2.C: Likewise.
---
 gcc/config/aarch64/aarch64.c                  | 159 +++++++++-----
 .../g++.target/aarch64/no_unique_address_1.C  | 206 ++++++++++++++++++
 .../g++.target/aarch64/no_unique_address_2.C  | 206 ++++++++++++++++++
 3 files changed, 518 insertions(+), 53 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/aarch64/no_unique_address_1.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/no_unique_address_2.C

Comments

Jakub Jelinek April 29, 2020, 10:09 a.m. UTC | #1
On Wed, Apr 29, 2020 at 10:57:24AM +0100, Richard Sandiford wrote:
> This patch makes the ABI code ignore zero-sized [[no_unique_address]]
> fields when deciding whether something is a HFA or HVA.

As you use cxx17_empty_base_field_p, can you please remove the FIXME
in calls.h because it then can't go away, and perhaps update it to
test DECL_ARTIFICIAL (field) instead (or in addition to no attribute,
with with DECL_ARTIFICIAL first)?

Thanks.

	Jakub
Richard Sandiford April 29, 2020, 10:48 a.m. UTC | #2
Jakub Jelinek <jakub@redhat.com> writes:
> On Wed, Apr 29, 2020 at 10:57:24AM +0100, Richard Sandiford wrote:
>> This patch makes the ABI code ignore zero-sized [[no_unique_address]]
>> fields when deciding whether something is a HFA or HVA.
>
> As you use cxx17_empty_base_field_p, can you please remove the FIXME
> in calls.h because it then can't go away, and perhaps update it to
> test DECL_ARTIFICIAL (field) instead (or in addition to no attribute,
> with with DECL_ARTIFICIAL first)?

Sure, how's this?  I added back the RECORD_OR_UNION_TYPE_P test too
for good measure :-)

Only lightly tested on aarch64-linux-gnu and aarch64_be-elf so far.

Richard


2020-04-29  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* calls.h (cxx17_empty_base_field_p): Turn into a function declaration.
	* calls.c (cxx17_empty_base_field_p): New function.  Check
	DECL_ARTIFICIAL and RECORD_OR_UNION_TYPE_P in addition to the
	previous checks.
---
 gcc/calls.c | 15 +++++++++++++++
 gcc/calls.h |  5 +----
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/gcc/calls.h b/gcc/calls.h
index e1c944efbb6..4ee49360777 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -135,9 +135,6 @@ extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern void maybe_warn_nonstring_arg (tree, tree);
 extern bool get_size_range (tree, tree[2], bool = false);
 extern rtx rtx_for_static_chain (const_tree, bool);
-/* FIXME: Remove after all backends are converted.  */
-#define cxx17_empty_base_field_p(t) \
-  (DECL_FIELD_ABI_IGNORED (t)						\
-   && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (t)))
+extern bool cxx17_empty_base_field_p (const_tree);
 
 #endif // GCC_CALLS_H
diff --git a/gcc/calls.c b/gcc/calls.c
index 5bd922779af..8041388c1d2 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -6261,5 +6261,20 @@ must_pass_va_arg_in_stack (tree type)
   return targetm.calls.must_pass_in_stack (arg);
 }
 
+/* Return true if FIELD is the C++17 empty base field that should
+   be ignored for ABI calling convention decisions in order to
+   maintain ABI compatibility between C++14 and earlier, which doesn't
+   add this FIELD to classes with empty bases, and C++17 and later
+   which does.  */
+
+bool
+cxx17_empty_base_field_p (const_tree field)
+{
+  return (DECL_FIELD_ABI_IGNORED (field)
+	  && DECL_ARTIFICIAL (field)
+	  && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
+	  && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (field)));
+}
+
 /* Tell the garbage collector about GTY markers in this source file.  */
 #include "gt-calls.h"
Jakub Jelinek April 29, 2020, 10:56 a.m. UTC | #3
On Wed, Apr 29, 2020 at 11:48:51AM +0100, Richard Sandiford wrote:
> Jakub Jelinek <jakub@redhat.com> writes:
> > On Wed, Apr 29, 2020 at 10:57:24AM +0100, Richard Sandiford wrote:
> >> This patch makes the ABI code ignore zero-sized [[no_unique_address]]
> >> fields when deciding whether something is a HFA or HVA.
> >
> > As you use cxx17_empty_base_field_p, can you please remove the FIXME
> > in calls.h because it then can't go away, and perhaps update it to
> > test DECL_ARTIFICIAL (field) instead (or in addition to no attribute,
> > with with DECL_ARTIFICIAL first)?
> 
> Sure, how's this?  I added back the RECORD_OR_UNION_TYPE_P test too
> for good measure :-)
> 
> Only lightly tested on aarch64-linux-gnu and aarch64_be-elf so far.
> 
> Richard
> 
> 
> 2020-04-29  Richard Sandiford  <richard.sandiford@arm.com>
> 
> gcc/
> 	* calls.h (cxx17_empty_base_field_p): Turn into a function declaration.
> 	* calls.c (cxx17_empty_base_field_p): New function.  Check
> 	DECL_ARTIFICIAL and RECORD_OR_UNION_TYPE_P in addition to the
> 	previous checks.

Ok, thanks.

	Jakub
diff mbox series

Patch

diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 5316350a9da..e996fd12042 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -286,7 +286,7 @@  static bool aarch64_return_in_memory_1 (const_tree);
 static bool aarch64_vfp_is_call_or_return_candidate (machine_mode,
 						     const_tree,
 						     machine_mode *, int *,
-						     bool *);
+						     bool *, bool);
 static void aarch64_elf_asm_constructor (rtx, int) ATTRIBUTE_UNUSED;
 static void aarch64_elf_asm_destructor (rtx, int) ATTRIBUTE_UNUSED;
 static void aarch64_override_options_after_change (void);
@@ -5369,7 +5369,8 @@  aarch64_function_ok_for_sibcall (tree, tree exp)
    passed in SVE registers.  */
 
 static bool
-aarch64_pass_by_reference_1 (const function_arg_info &arg)
+aarch64_pass_by_reference_1 (CUMULATIVE_ARGS *pcum,
+			     const function_arg_info &arg)
 {
   HOST_WIDE_INT size;
   machine_mode dummymode;
@@ -5393,8 +5394,8 @@  aarch64_pass_by_reference_1 (const function_arg_info &arg)
 
   /* Can this be a candidate to be passed in fp/simd register(s)?  */
   if (aarch64_vfp_is_call_or_return_candidate (arg.mode, arg.type,
-					       &dummymode, &nregs,
-					       NULL))
+					       &dummymode, &nregs, NULL,
+					       !pcum || pcum->silent_p))
     return false;
 
   /* Arguments which are variable sized or larger than 2 registers are
@@ -5412,7 +5413,7 @@  aarch64_pass_by_reference (cumulative_args_t pcum_v,
   CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
 
   if (!arg.type)
-    return aarch64_pass_by_reference_1 (arg);
+    return aarch64_pass_by_reference_1 (pcum, arg);
 
   pure_scalable_type_info pst_info;
   switch (pst_info.analyze (arg.type))
@@ -5431,12 +5432,12 @@  aarch64_pass_by_reference (cumulative_args_t pcum_v,
 	      || pcum->aapcs_nprn + pst_info.num_pr () > NUM_PR_ARG_REGS);
 
     case pure_scalable_type_info::DOESNT_MATTER:
-      gcc_assert (aarch64_pass_by_reference_1 (arg));
+      gcc_assert (aarch64_pass_by_reference_1 (pcum, arg));
       return true;
 
     case pure_scalable_type_info::NO_ABI_IDENTITY:
     case pure_scalable_type_info::ISNT_PST:
-      return aarch64_pass_by_reference_1 (arg);
+      return aarch64_pass_by_reference_1 (pcum, arg);
     }
   gcc_unreachable ();
 }
@@ -5464,7 +5465,8 @@  aarch64_return_in_msb (const_tree valtype)
      is always passed/returned in the least significant bits of fp/simd
      register(s).  */
   if (aarch64_vfp_is_call_or_return_candidate (TYPE_MODE (valtype), valtype,
-					       &dummy_mode, &dummy_int, NULL))
+					       &dummy_mode, &dummy_int, NULL,
+					       false))
     return false;
 
   /* Likewise pure scalable types for SVE vector and predicate registers.  */
@@ -5511,8 +5513,8 @@  aarch64_function_value (const_tree type, const_tree func,
 
   int count;
   machine_mode ag_mode;
-  if (aarch64_vfp_is_call_or_return_candidate (mode, type,
-					       &ag_mode, &count, NULL))
+  if (aarch64_vfp_is_call_or_return_candidate (mode, type, &ag_mode, &count,
+					       NULL, false))
     {
       gcc_assert (!sve_p);
       if (!aarch64_composite_type_p (type, mode))
@@ -5599,11 +5601,8 @@  aarch64_return_in_memory_1 (const_tree type)
     /* Simple scalar types always returned in registers.  */
     return false;
 
-  if (aarch64_vfp_is_call_or_return_candidate (TYPE_MODE (type),
-					       type,
-					       &ag_mode,
-					       &count,
-					       NULL))
+  if (aarch64_vfp_is_call_or_return_candidate (TYPE_MODE (type), type,
+					       &ag_mode, &count, NULL, false))
     return false;
 
   /* Types larger than 2 registers returned in memory.  */
@@ -5646,11 +5645,9 @@  aarch64_vfp_is_call_candidate (cumulative_args_t pcum_v, machine_mode mode,
 			       const_tree type, int *nregs)
 {
   CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
-  return aarch64_vfp_is_call_or_return_candidate (mode,
-						  type,
+  return aarch64_vfp_is_call_or_return_candidate (mode, type,
 						  &pcum->aapcs_vfp_rmode,
-						  nregs,
-						  NULL);
+						  nregs, NULL, pcum->silent_p);
 }
 
 /* Given MODE and TYPE of a function argument, return the alignment in
@@ -5684,6 +5681,19 @@  aarch64_function_arg_alignment (machine_mode mode, const_tree type,
   for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
     if (TREE_CODE (field) == FIELD_DECL)
       {
+	/* Note that we explicitly consider zero-sized fields here,
+	   even though they don't map to AAPCS64 machine types.
+	   For example, in:
+
+	       struct __attribute__((aligned(8))) empty {};
+
+	       struct s {
+		 [[no_unique_address]] empty e;
+		 int x;
+	       };
+
+	   "s" contains only one Fundamental Data Type (the int field)
+	   but gains 8-byte alignment and size thanks to "e".  */
 	alignment = std::max (alignment, DECL_ALIGN (field));
 	if (DECL_BIT_FIELD_TYPE (field))
 	  bitfield_alignment
@@ -5976,7 +5986,7 @@  aarch64_init_cumulative_args (CUMULATIVE_ARGS *pcum,
       machine_mode mode ATTRIBUTE_UNUSED; /* To pass pointer as argument.  */
       int nregs ATTRIBUTE_UNUSED; /* Likewise.  */
       if (aarch64_vfp_is_call_or_return_candidate (TYPE_MODE (type), type,
-						   &mode, &nregs, NULL))
+						   &mode, &nregs, NULL, false))
 	aarch64_err_no_fpadvsimd (TYPE_MODE (type));
     }
 
@@ -16152,11 +16162,8 @@  aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
 
   dw_align = false;
   adjust = 0;
-  if (aarch64_vfp_is_call_or_return_candidate (mode,
-					       type,
-					       &ag_mode,
-					       &nregs,
-					       &is_ha))
+  if (aarch64_vfp_is_call_or_return_candidate (mode, type, &ag_mode, &nregs,
+					       &is_ha, false))
     {
       /* No frontends can create types with variable-sized modes, so we
 	 shouldn't be asked to pass or return them.  */
@@ -16521,23 +16528,42 @@  aarch64_member_type_forces_blk (const_tree field_or_array, machine_mode mode)
   return default_member_type_forces_blk (field_or_array, mode);
 }
 
+/* Bitmasks that indicate whether earlier versions of GCC would have
+   taken a different path through the ABI logic.  This should result in
+   a -Wpsabi warning if the earlier path led to a different ABI decision.
+
+   WARN_PSABI_EMPTY_CXX17_BASE
+      Indicates that the type includes an artificial empty C++17 base field
+      that, prior to GCC 10.1, would prevent the type from being treated as
+      a HFA or HVA.  See PR94383 for details.
+
+   WARN_PSABI_NO_UNIQUE_ADDRESS
+      Indicates that the type includes an empty [[no_unique_address]] field
+      that, prior to GCC 10.1, would prevent the type from being treated as
+      a HFA or HVA.  */
+const unsigned int WARN_PSABI_EMPTY_CXX17_BASE = 1U << 0;
+const unsigned int WARN_PSABI_NO_UNIQUE_ADDRESS = 1U << 1;
+
 /* Walk down the type tree of TYPE counting consecutive base elements.
    If *MODEP is VOIDmode, then set it to the first valid floating point
    type.  If a non-floating point type is found, or if a floating point
    type that doesn't match a non-VOIDmode *MODEP is found, then return -1,
    otherwise return the count in the sub-tree.
 
-   The AVOID_CXX17_EMPTY_BASE argument is to allow the caller to check whether
-   this function has changed its behavior after the fix for PR94384 -- this fix
-   is to avoid artificial fields in empty base classes.
-   When called with this argument as a NULL pointer this function does not
-   avoid the artificial fields -- this is useful to check whether the function
-   returns something different after the fix.
-   When called pointing at a value, this function avoids such artificial fields
-   and sets the value to TRUE when one of these fields has been set.  */
+   The WARN_PSABI_FLAGS argument allows the caller to check whether this
+   function has changed its behavior relative to earlier versions of GCC.
+   Normally the argument should be nonnull and point to a zero-initialized
+   variable.  The function then records whether the ABI decision might
+   be affected by a known fix to the ABI logic, setting the associated
+   WARN_PSABI_* bits if so.
+
+   When the argument is instead a null pointer, the function tries to
+   simulate the behavior of GCC before all such ABI fixes were made.
+   This is useful to check whether the function returns something
+   different after the ABI fixes.  */
 static int
 aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
-			 bool *avoid_cxx17_empty_base)
+			 unsigned int *warn_psabi_flags)
 {
   machine_mode mode;
   HOST_WIDE_INT size;
@@ -16614,7 +16640,7 @@  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
 	  return -1;
 
 	count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep,
-					 avoid_cxx17_empty_base);
+					 warn_psabi_flags);
 	if (count == -1
 	    || !index
 	    || !TYPE_MAX_VALUE (index)
@@ -16652,18 +16678,30 @@  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
 	    if (TREE_CODE (field) != FIELD_DECL)
 	      continue;
 
-	    /* Ignore C++17 empty base fields, while their type indicates
-	       they do contain padding, they have zero size and thus don't
-	       contain any padding.  */
-	    if (cxx17_empty_base_field_p (field)
-		&& avoid_cxx17_empty_base)
+	    if (DECL_FIELD_ABI_IGNORED (field))
 	      {
-		*avoid_cxx17_empty_base = true;
-		continue;
+		/* See whether this is something that earlier versions of
+		   GCC failed to ignore.  */
+		unsigned int flag;
+		if (lookup_attribute ("no_unique_address",
+				      DECL_ATTRIBUTES (field)))
+		  flag = WARN_PSABI_NO_UNIQUE_ADDRESS;
+		else if (cxx17_empty_base_field_p (field))
+		  flag = WARN_PSABI_EMPTY_CXX17_BASE;
+		else
+		  /* No compatibility problem.  */
+		  continue;
+
+		/* Simulate the old behavior when WARN_PSABI_FLAGS is null.  */
+		if (warn_psabi_flags)
+		  {
+		    *warn_psabi_flags |= flag;
+		    continue;
+		  }
 	      }
 
 	    sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
-						 avoid_cxx17_empty_base);
+						 warn_psabi_flags);
 	    if (sub_count < 0)
 	      return -1;
 	    count += sub_count;
@@ -16697,7 +16735,7 @@  aapcs_vfp_sub_candidate (const_tree type, machine_mode *modep,
 	      continue;
 
 	    sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep,
-						 avoid_cxx17_empty_base);
+						 warn_psabi_flags);
 	    if (sub_count < 0)
 	      return -1;
 	    count = count > sub_count ? count : sub_count;
@@ -16796,14 +16834,20 @@  aarch64_composite_type_p (const_tree type,
    Upon successful return, *COUNT returns the number of needed registers,
    *BASE_MODE returns the mode of the individual register and when IS_HAF
    is not NULL, *IS_HA indicates whether or not the argument is a homogeneous
-   floating-point aggregate or a homogeneous short-vector aggregate.  */
+   floating-point aggregate or a homogeneous short-vector aggregate.
+
+   SILENT_P is true if the function should refrain from reporting any
+   diagnostics.  This should only be used if the caller is certain that
+   any ABI decisions would eventually come through this function with
+   SILENT_P set to false.  */
 
 static bool
 aarch64_vfp_is_call_or_return_candidate (machine_mode mode,
 					 const_tree type,
 					 machine_mode *base_mode,
 					 int *count,
-					 bool *is_ha)
+					 bool *is_ha,
+					 bool silent_p)
 {
   if (is_ha != NULL) *is_ha = false;
 
@@ -16824,24 +16868,33 @@  aarch64_vfp_is_call_or_return_candidate (machine_mode mode,
     }
   else if (type && composite_p)
     {
-      bool avoided = false;
-      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode, &avoided);
+      unsigned int warn_psabi_flags = 0;
+      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode,
+					      &warn_psabi_flags);
       if (ag_count > 0 && ag_count <= HA_MAX_NUM_FLDS)
 	{
 	  static unsigned last_reported_type_uid;
 	  unsigned uid = TYPE_UID (TYPE_MAIN_VARIANT (type));
 	  int alt;
-	  if (warn_psabi
-	      && avoided
+	  if (!silent_p
+	      && warn_psabi
+	      && warn_psabi_flags
 	      && uid != last_reported_type_uid
 	      && ((alt = aapcs_vfp_sub_candidate (type, &new_mode, NULL))
 		  != ag_count))
 	    {
 	      gcc_assert (alt == -1);
 	      last_reported_type_uid = uid;
-	      inform (input_location, "parameter passing for argument of type "
-		      "%qT when C++17 is enabled changed to match C++14 "
-		      "in GCC 10.1", type);
+	      /* Use TYPE_MAIN_VARIANT to strip any redundant const
+		 qualification.  */
+	      if (warn_psabi_flags & WARN_PSABI_NO_UNIQUE_ADDRESS)
+		inform (input_location, "parameter passing for argument of "
+			"type %qT with %<[[no_unique_address]]%> members "
+			"changed in GCC 10.1", TYPE_MAIN_VARIANT (type));
+	      else if (warn_psabi_flags & WARN_PSABI_EMPTY_CXX17_BASE)
+		inform (input_location, "parameter passing for argument of "
+			"type %qT when C++17 is enabled changed to match "
+			"C++14 in GCC 10.1", TYPE_MAIN_VARIANT (type));
 	    }
 
 	  if (is_ha != NULL) *is_ha = true;
diff --git a/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C b/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C
new file mode 100644
index 00000000000..5fc68ea5d6d
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/no_unique_address_1.C
@@ -0,0 +1,206 @@ 
+/* { dg-options "-std=c++11 -O -foptimize-sibling-calls -fpeephole2" } */
+/* { dg-final { check-function-bodies "**" "" "" { target lp64 } } } */
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct W : public X { X q; };
+
+struct A { float a; };
+
+struct B : public X { float a; };
+struct C : public Y { float a; };
+struct D : public Z { float a; };
+struct E : public W { float a; };
+
+struct F { [[no_unique_address]] X x; float a; };
+struct G { [[no_unique_address]] Y y; float a; };
+struct H { [[no_unique_address]] Z z; float a; };
+struct I { [[no_unique_address]] W w; float a; };
+
+struct J { float a; [[no_unique_address]] X x; float b; };
+struct K { float a; [[no_unique_address]] Y y; float b; };
+struct L { float a; [[no_unique_address]] Z z; float b; };
+struct M { float a; [[no_unique_address]] W w; float b; };
+
+struct N : public A { float b; };
+struct O { [[no_unique_address]] A a; float b; };
+
+struct P : public Y { int : 0; float a, b, c, d; };
+
+union Q { X x; float a; };
+union R { [[no_unique_address]] X x; float a; };
+
+union S { A a; float b; };
+union T { F f; float b; };
+union U { N n; O o; };
+
+typedef S Salias;
+typedef T Talias;
+typedef U Ualias;
+
+#define T(S, s) extern int callee_##s (S)
+
+/*
+** _Z8caller_aR1A:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (A, a); int caller_a (A &a) { return callee_a (a); } /* { dg-bogus {argument of type 'A'} } */
+
+/*
+** _Z8caller_bR1B:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (B, b); int caller_b (B &b) { return callee_b (b); } /* { dg-bogus {argument of type 'B'} } */
+
+/*
+** _Z8caller_cR1C:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (C, c); int caller_c (C &c) { return callee_c (c); } /* { dg-bogus {argument of type 'C'} } */
+
+/*
+** _Z8caller_dR1D:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (D, d); int caller_d (D &d) { return callee_d (d); } /* { dg-bogus {argument of type 'D'} } */
+
+/*
+** _Z8caller_eR1E:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (E, e); int caller_e (E &e) { return callee_e (e); } /* { dg-bogus {argument of type 'E'} } */
+
+/*
+** _Z8caller_fR1F:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (F, f); int caller_f (F &f) { return callee_f (f); } /* { dg-message {parameter passing for argument of type 'F' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_gR1G:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (G, g); int caller_g (G &g) { return callee_g (g); } /* { dg-message {parameter passing for argument of type 'G' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_hR1H:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (H, h); int caller_h (H &h) { return callee_h (h); } /* { dg-bogus {argument of type 'H'} } */
+
+/*
+** _Z8caller_iR1I:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (I, i); int caller_i (I &i) { return callee_i (i); } /* { dg-bogus {argument of type 'I'} } */
+
+/*
+** _Z8caller_jR1J:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (J, j); int caller_j (J &j) { return callee_j (j); } /* { dg-message {parameter passing for argument of type 'J' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_kR1K:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (K, k); int caller_k (K &k) { return callee_k (k); } /* { dg-message {parameter passing for argument of type 'K' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_lR1L: { target aarch64_little_endian }
+** (
+**	ldr	w1, \[x0, 8\]
+**	ldr	x0, \[x0\]
+** |
+**	mov	(x[0-9]+), x0
+**	ldr	x0, \[x0\]
+**	ldr	w1, \[\1, 8\]
+** )
+**	b	.*
+*/
+T (L, l); int caller_l (L &l) { return callee_l (l); } /* { dg-bogus {argument of type 'L'} } */
+
+/*
+** _Z8caller_mR1M: { target aarch64_little_endian }
+** (
+**	ldr	w1, \[x0, 8\]
+**	ldr	x0, \[x0\]
+** |
+**	mov	(x[0-9]+), x0
+**	ldr	x0, \[x0\]
+**	ldr	w1, \[\1, 8\]
+** )
+**	b	.*
+*/
+T (M, m); int caller_m (M &m) { return callee_m (m); } /* { dg-bogus {argument of type 'M'} } */
+
+/*
+** _Z8caller_nR1N:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (N, n); int caller_n (N &n) { return callee_n (n); } /* { dg-bogus {argument of type 'N'} } */
+
+/*
+** _Z8caller_oR1O:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (O, o); int caller_o (O &o) { return callee_o (o); } /* { dg-bogus {argument of type 'O'} } */
+
+/*
+** _Z8caller_pR1P:
+**	ldp	s0, s1, \[x0\]
+**	ldp	s2, s3, \[x0, 8\]
+**	b	.*
+*/
+T (P, p); int caller_p (P &p) { return callee_p (p); } /* { dg-bogus {argument of type 'P'} } */
+
+/*
+** _Z8caller_qR1Q: { target aarch64_little_endian }
+**	ldr	w0, \[x0\]
+**	b	.*
+*/
+T (Q, q); int caller_q (Q &q) { return callee_q (q); } /* { dg-bogus {argument of type 'Q'} } */
+
+/*
+** _Z8caller_rR1R: { target aarch64_little_endian }
+**	ldr	w0, \[x0\]
+**	b	.*
+*/
+T (R, r); int caller_r (R &r) { return callee_r (r); } /* { dg-bogus {argument of type 'R'} } */
+
+/*
+** _Z8caller_sR1S:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (Salias, s); int caller_s (Salias &s) { return callee_s (s); } /* { dg-bogus {argument of type 'S'} } */
+
+/*
+** _Z8caller_tR1T:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (Talias, t); int caller_t (Talias &t) { return callee_t (t); } /* { dg-message {parameter passing for argument of type 'T' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_uR1U:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (Ualias, u); int caller_u (Ualias &u) { return callee_u (u); } /* { dg-bogus {argument of type 'U'} } */
+
+/* { dg-bogus {argument of type 'const} "should not be printed as const" { target *-*-*} 0 } */
diff --git a/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C b/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C
new file mode 100644
index 00000000000..f0717133ccd
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/no_unique_address_2.C
@@ -0,0 +1,206 @@ 
+/* { dg-options "-std=c++17 -O -foptimize-sibling-calls -fpeephole2" } */
+/* { dg-final { check-function-bodies "**" "" "" { target lp64 } } } */
+
+struct X { };
+struct Y { int : 0; };
+struct Z { int : 0; Y y; };
+struct W : public X { X q; };
+
+struct A { float a; };
+
+struct B : public X { float a; };
+struct C : public Y { float a; };
+struct D : public Z { float a; };
+struct E : public W { float a; };
+
+struct F { [[no_unique_address]] X x; float a; };
+struct G { [[no_unique_address]] Y y; float a; };
+struct H { [[no_unique_address]] Z z; float a; };
+struct I { [[no_unique_address]] W w; float a; };
+
+struct J { float a; [[no_unique_address]] X x; float b; };
+struct K { float a; [[no_unique_address]] Y y; float b; };
+struct L { float a; [[no_unique_address]] Z z; float b; };
+struct M { float a; [[no_unique_address]] W w; float b; };
+
+struct N : public A { float b; };
+struct O { [[no_unique_address]] A a; float b; };
+
+struct P : public Y { int : 0; float a, b, c, d; };
+
+union Q { X x; float a; };
+union R { [[no_unique_address]] X x; float a; };
+
+union S { A a; float b; };
+union T { F f; float b; };
+union U { N n; O o; };
+
+typedef S Salias;
+typedef T Talias;
+typedef U Ualias;
+
+#define T(S, s) extern int callee_##s (S)
+
+/*
+** _Z8caller_aR1A:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (A, a); int caller_a (A &a) { return callee_a (a); } /* { dg-bogus {argument of type 'A'} } */
+
+/*
+** _Z8caller_bR1B:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (B, b); int caller_b (B &b) { return callee_b (b); } /* { dg-message {parameter passing for argument of type 'B' when C\+\+17 is enabled changed to match C\+\+14 in GCC 10.1} } */
+
+/*
+** _Z8caller_cR1C:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (C, c); int caller_c (C &c) { return callee_c (c); } /* { dg-message {parameter passing for argument of type 'C' when C\+\+17 is enabled changed to match C\+\+14 in GCC 10.1} } */
+
+/*
+** _Z8caller_dR1D:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (D, d); int caller_d (D &d) { return callee_d (d); } /* { dg-bogus {argument of type 'D'} } */
+
+/*
+** _Z8caller_eR1E:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (E, e); int caller_e (E &e) { return callee_e (e); } /* { dg-bogus {argument of type 'E'} } */
+
+/*
+** _Z8caller_fR1F:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (F, f); int caller_f (F &f) { return callee_f (f); } /* { dg-message {parameter passing for argument of type 'F' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_gR1G:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (G, g); int caller_g (G &g) { return callee_g (g); } /* { dg-message {parameter passing for argument of type 'G' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_hR1H:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (H, h); int caller_h (H &h) { return callee_h (h); } /* { dg-bogus {argument of type 'H'} } */
+
+/*
+** _Z8caller_iR1I:
+**	ldr	x0, \[x0\]
+**	b	.*
+*/
+T (I, i); int caller_i (I &i) { return callee_i (i); } /* { dg-bogus {argument of type 'I'} } */
+
+/*
+** _Z8caller_jR1J:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (J, j); int caller_j (J &j) { return callee_j (j); } /* { dg-message {parameter passing for argument of type 'J' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_kR1K:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (K, k); int caller_k (K &k) { return callee_k (k); } /* { dg-message {parameter passing for argument of type 'K' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_lR1L: { target aarch64_little_endian }
+** (
+**	ldr	w1, \[x0, 8\]
+**	ldr	x0, \[x0\]
+** |
+**	mov	(x[0-9]+), x0
+**	ldr	x0, \[x0\]
+**	ldr	w1, \[\1, 8\]
+** )
+**	b	.*
+*/
+T (L, l); int caller_l (L &l) { return callee_l (l); } /* { dg-bogus {argument of type 'L'} } */
+
+/*
+** _Z8caller_mR1M: { target aarch64_little_endian }
+** (
+**	ldr	w1, \[x0, 8\]
+**	ldr	x0, \[x0\]
+** |
+**	mov	(x[0-9]+), x0
+**	ldr	x0, \[x0\]
+**	ldr	w1, \[\1, 8\]
+** )
+**	b	.*
+*/
+T (M, m); int caller_m (M &m) { return callee_m (m); } /* { dg-bogus {argument of type 'M'} } */
+
+/*
+** _Z8caller_nR1N:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (N, n); int caller_n (N &n) { return callee_n (n); } /* { dg-bogus {argument of type 'N'} } */
+
+/*
+** _Z8caller_oR1O:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (O, o); int caller_o (O &o) { return callee_o (o); } /* { dg-bogus {argument of type 'O'} } */
+
+/*
+** _Z8caller_pR1P:
+**	ldp	s0, s1, \[x0\]
+**	ldp	s2, s3, \[x0, 8\]
+**	b	.*
+*/
+T (P, p); int caller_p (P &p) { return callee_p (p); } /* { dg-message {parameter passing for argument of type 'P' when C\+\+17 is enabled changed to match C\+\+14 in GCC 10.1} } */
+
+/*
+** _Z8caller_qR1Q: { target aarch64_little_endian }
+**	ldr	w0, \[x0\]
+**	b	.*
+*/
+T (Q, q); int caller_q (Q &q) { return callee_q (q); } /* { dg-bogus {argument of type 'Q'} } */
+
+/*
+** _Z8caller_rR1R: { target aarch64_little_endian }
+**	ldr	w0, \[x0\]
+**	b	.*
+*/
+T (R, r); int caller_r (R &r) { return callee_r (r); } /* { dg-bogus {argument of type 'R'} } */
+
+/*
+** _Z8caller_sR1S:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (Salias, s); int caller_s (Salias &s) { return callee_s (s); } /* { dg-bogus {argument of type 'S'} } */
+
+/*
+** _Z8caller_tR1T:
+**	ldr	s0, \[x0\]
+**	b	.*
+*/
+T (Talias, t); int caller_t (Talias &t) { return callee_t (t); } /* { dg-message {parameter passing for argument of type 'T' with '\[\[no_unique_address\]\]' members changed in GCC 10.1} } */
+
+/*
+** _Z8caller_uR1U:
+**	ldp	s0, s1, \[x0\]
+**	b	.*
+*/
+T (Ualias, u); int caller_u (Ualias &u) { return callee_u (u); } /* { dg-bogus {argument of type 'U'} } */
+
+/* { dg-bogus {argument of type 'const} "should not be printed as const" { target *-*-*} 0 } */